Templates Are Circuit Maps: How CKE Describes A Model Family

C-Kernel-Engine hardening note

A CKE template is not decoration. It is the circuit map that tells the compiler what model path exists before any C code is emitted.

In C-Kernel-Engine, model support should not begin with a pile of special cases hidden inside generated C. It should begin with an explicit circuit map. That is what the template layer is for.

A template says: this model family has this tokenizer contract, this attention contract, this block shape, this operation order, these branch points, these output rules, and these runtime invariants. It does not claim the model is fast. It does not claim the model is numerically correct. It gives the compiler a concrete architecture contract that can be audited before lowering starts.

This post is part of the CKE v8 hardening arc. If you are new to the project, the broader primer is What Is the C-Kernel-Engine?. This note zooms into one piece of that system: how model-family templates turn architecture into something the runtime can inspect.

The implementation discussed here is public. Start with the v8 template directory, its template contract, and the template-to-IR pipeline. The concrete examples in this post are grounded in those artifacts rather than in a generic compiler diagram.

Static CKE template circuit showing the path from template JSON to generated C and validation evidence Interactive explainer

Follow the JSON circuit from template to generated C

The trick is not that JSON is magical. The trick is that the JSON has a strict circuit contract: named stages, named streams, named tensors, named operations, and audit points. That is what gives CKE something an agent, a human, and a compiler can all inspect.

Interactive CKE template circuit map Template JSON flows into architecture contract, GraphIR, LoweredIR, generated C, and parity plus profiling evidence. Template JSON sequence block_types Architecture Contract RoPE / MLP attention rules GraphIR symbolic nodes tensor edges graph slots LoweredIR buffer slots dtype views kernel ids Generated C pointer calls loop order shared object Parity + Profiling smoke, diff, VTune evidence before claims Invariant The circuit must stay legible before CKE makes it fast.

Stage 1 of 6

Template JSON declares the family.

The template is the first artifact where the model stops being folklore. It names the block order, family flags, graph slots, and operation sequence before any generated C exists.

4semantic slots 11declared ops 0selected kernels 0evidence gates
{
  "sequence": ["decoder"],
  "body": ["rmsnorm", "qkv_proj", "rope_qk"]
}
Comic character explaining CKE templates as circuit maps
The useful mental model: a CKE template is a readable circuit map before the compiler starts generating execution artifacts.

The Short Version

QuestionAnswerCKE artifact
What does a template describe?The architecture-level operation circuit for a model family.version/v8/templates/*.json
What does it not describe?Weights, physical memory offsets, final kernel selection, or allocator layout.Those belong to manifests, registry, LoweredIR, and memory planner.
Why does it matter?It prevents model support from becoming scattered branch logic.Template audit, GraphIR, LoweredIR, generated C.
What is the hardening rule?Support means evidence, not a demo.Parity, smoke, profiler, and circuit audit reports.

Template, GraphIR, LoweredIR, C

The compact CKE path is:

$$ \text{Template} \rightarrow \text{GraphIR} \rightarrow \text{LoweredIR} \rightarrow \text{Generated C} $$

This is more than a pretty flowchart. Each stage has a different job.

Diagram showing templates becoming GraphIR, LoweredIR, registry selection, memory planning, generated C, and parity checks
The template is the architecture contract. GraphIR wires symbolic tensors. LoweredIR selects concrete kernel behavior. Codegen emits the executable statement of those choices.
StageQuestion it answersWhat should stay visible
TemplateWhat architecture circuit does this model family need?Sequence, block types, ops, contracts, branch declarations.
GraphIRHow do symbolic tensors flow between declared operations?Inputs, outputs, stable names, branch taps, collect/stitch points.
LoweredIRWhich concrete runtime operations and kernels should execute?Kernel IDs, dtype decisions, physical activation views, buffer slots.
Memory plannerWhere do tensors live and when can storage be reused?Persistent buffers, scratch arenas, KV cache, lifetimes.
Generated CWhat does the runtime actually execute?Pointer expressions, call order, strides, kernel calls, constants.
Parity/profilingDid it match reference behavior and where is time going?Diffs, smoke reports, VTune/Advisor/perf artifacts.

Why Templates Exist

The v8 template README in CKE says the quiet but important thing: graph templates are the missing piece between the weights manifest and the kernel registry. The weights manifest tells CKE what tensors exist. The kernel registry tells CKE what kernels can execute. The template tells CKE what the model is trying to do.

You can see that separation directly in the repository: qwen3.json declares the Qwen3 family circuit, while KERNEL_REGISTRY.json indexes executable kernel contracts. The template contains operations such as qkv_proj, qk_norm, rope_qk, and attn; it does not contain the AVX2 implementation of those operations.

Without that middle layer, every model family becomes a pile of assumptions. Qwen, Gemma, GLM, Nemotron, Kimi, SigLIP, and Qwen3-VL do not all have the same tokenizer rules, attention rules, projection layout, multimodal bridge, or output contract. If the runtime silently assumes a generic transformer, the first successful compile may still produce nonsense.

That is the exact failure mode templates are meant to reduce. The template makes the model-family claim reviewable.

What A CKE Template Does

A v8 template can describe:

  • the top-level execution sequence, such as decoder-only or vision-then-decoder
  • block phases such as header, body, and footer
  • the per-layer operation sequence
  • family flags such as activation type, RoPE type, tokenizer type, and bias policy
  • attention contracts such as dense attention, sliding attention, MRoPE, QK norm, or KV-cache layout
  • chat and tokenizer contracts that affect whether generated text is even meaningful
  • runtime invariants such as required call arguments for KV-copy operations
  • multimodal bridge policies for image markers, visual-text prefill, and staged decode
  • kernel hints for family-specific attention, RoPE, or decode paths
  • branch taps, collect targets, and stitch points for fixed side paths

That list matters because most bugs do not announce themselves as "wrong architecture." They show up as gibberish output, slightly wrong logits, unstable parity, bad first-token behavior, or a fast kernel attached to the wrong buffer.

What A Template Does Not Do

It is equally important to keep the template small enough to remain readable.

Not the template's jobOwner in CKEWhy the separation matters
Store weights or offsetsWeights manifest and model mapsThe same architecture can have different tensor files and quant formats.
Choose final kernelsKernel registry and selection layerKernel choice depends on dtype, shape, ISA, and layout.
Define physical memory layoutLoweredIR and memory plannerArchitecture order is not the same as arena allocation.
Hide model-family branches in codeTemplate control primitivesBranching should be declared as graph structure, not scattered lowerer logic.
Prove correctnessParity and audit gatesA correct-looking circuit can still lower into wrong math.

This is the discipline I want CKE to keep: the template describes the circuit, but it does not become a god object.

A Simplified Decoder Template

A simplified decoder-only circuit looks like this:

{
  "version": 2,
  "name": "example_decoder_family",
  "flags": {
    "activation": "swiglu",
    "rope": "rope",
    "tokenizer": "bpe"
  },
  "sequence": ["decoder"],
  "block_types": {
    "decoder": {
      "sequence": ["header", "body", "footer"],
      "header": [
        "bpe_tokenizer",
        "dense_embedding_lookup"
      ],
      "body": {
        "type": "dense",
        "ops": [
          "rmsnorm",
          "qkv_proj",
          "rope_qk",
          "attn",
          "out_proj",
          "residual_add",
          "rmsnorm",
          "mlp_gate_up",
          "silu_mul",
          "mlp_down",
          "residual_add"
        ]
      },
      "footer": [
        "rmsnorm",
        "lm_head",
        "logits"
      ]
    }
  }
}

The important part is not JSON. The important part is the contract. The compiler does not need to infer that attention comes before the output projection. The sequence says so. The compiler does not need to guess whether the MLP is gate-up, activation, down. The sequence says so. The template is the first artifact where the model stops being folklore and becomes an explicit circuit.

JSON Is The Circuit Language, Not The Runtime

This is where people can misunderstand the idea. CKE is not trying to run a model from JSON. JSON is not the runtime. JSON is the circuit language that lets the runtime describe what it is about to build. That distinction matters.

A hardware circuit diagram does not carry the current. The physical board carries the current. But the diagram tells the engineer where the current is supposed to flow. A CKE template works the same way. It does not multiply matrices. It tells CKE which stream is normalized, which stream feeds attention, which stream feeds the MLP, where residual additions happen, and where the output logits come from.

Once that circuit is declared, CKE can do useful compiler work:

  • validate whether required operations exist for the model family
  • bind GGUF or internal weight names to semantic tensor slots
  • emit GraphIR nodes with stable symbolic input and output names
  • lower symbolic streams into physical buffer views and kernel calls
  • choose prefill, decode, or multimodal bridge plans over the same family circuit
  • audit generated C against the intended circuit instead of trusting a demo

This is also why the template format is friendly to AI-assisted engineering. The agent should not be randomly rewriting the C runtime when it wants to add a family rule. It should patch the circuit contract, run the template audit, regenerate IR, inspect the visualizer, run smoke/parity, and only then touch the lower layers if the evidence says the lower layers are actually wrong.

Static atlas comparing decoder, encoder, recurrent, interleaved-attention, and multimodal CKE template circuits Template atlas

Different model families are different circuits

The template layer has to describe more than one transformer shape. A plain decoder, a vision encoder, an encoder-decoder, an interleaved attention stack, and a recurrent DeltaNet/SSM path are not the same circuit. They may share kernels, but their dataflow contracts are different.

Interactive atlas of CKE template circuits Selectable circuit diagrams for decoder, encoder, encoder-decoder, interleaved attention, recurrent memory, vision bridge, and DSL generation templates. Input tokens ids RMSNorm residual stream normalized view Attention Q K V KV cache MLP gate/up down proj Output logits next token Side Path state / memory bridge / DSL

Decoder-only template

Tokens become logits through one repeated block.

This is the familiar path: embedding, norm, attention, residual, MLP, residual, final norm, logits. Even here, the template must preserve stream names, RoPE rules, KV-cache policy, and output contract.

  • core question: which stream feeds QKV?
  • hardening check: logits and first-token behavior
{
  "sequence": ["decoder"],
  "body": ["rmsnorm", "qkv_proj", "rope_qk", "attn", "mlp"]
}

What The Template Has To Preserve

The practical template question is not "can I name the architecture?" The practical question is: what has to survive the journey from paper architecture to CKE execution artifact?

Template familyWhat the circuit must preserveWhat CKE should inspect
Ordinary decoderEmbedding, residual stream, norm, QKV, RoPE, attention, MLP, final logits.First-token behavior, KV-cache shape, logits input, tokenizer/chat contract.
EncoderPatch/frame/features into dense embeddings with bidirectional context.Input preprocessing, positional policy, pooled/projected embedding shape.
Encoder-decoderEncoder memory and decoder causal stream as separate memory surfaces.Cross-attention K/V source, decoder self-attention mask, timestamp/output rules.
Interleaved attentionLayer schedule for full attention, sliding/local attention, and RoPE variants.Which layers keep global memory, which layers use local windows, and how KV cache is allocated.
DeltaNet / SSM / MambaResidual stream, split stream, conv1d, dt softplus, recurrent state, selective scan.State update math, scan order, graph slots, and separation of recurrent state from residual state.
Vision-language bridgeVision encoder output, projection, marker insertion, MRoPE, visual-text prefill.Image token positions, bridge shape, decoder handoff, staged decode.
Training DSLDataset contract, tokenizer, model spec, loss, evaluator, and artifact lineage.Data validity, eval definition, model dimensions, generated dataset visualizer, IR report.
Static CKE parameter map connecting datasets, tokenizers, model dimensions, circuit operations, memory, evaluation, and runtime artifacts Parameter inspector

Click the circuit parameters: what does the template actually mean?

A useful template is not a flat list of buzzwords. Each parameter has a job. Some parameters describe the data, some describe the model circuit, some describe memory, and some describe evidence. CKE needs these fields because the same model name can hide very different execution requirements.

Interactive CKE template and DSL parameter inspector Clickable parameter nodes explain dataset, tokenizer, model shape, circuit ops, graph slots, memory policy, lowering hints, evaluation, and artifacts. dataset what examples are we using? tokenizer symbols into ids ids into tensors model d_model / layers heads / vocab circuit ops norm / attn mlp / scan graph slots semantic streams named edges memory policy KV / state arena / lifetime lowering hints dtype / ISA kernel family eval what proves it worked? artifacts IR / report dataset viz runtime CKE C executes

dataset

Defines the training or inference examples.

The dataset parameter says what the system is actually looking at: SVG DSL pairs, audio frames, image patches, token sequences, benchmark prompts, or hardware traces. Without this, the rest of the pipeline can look precise while training on an undefined target.

QuestionWhat examples are valid? CKE impactShapes the tokenizer, batches, and eval. Human checkCan I inspect examples quickly? Artifactdataset manifest + visualizer
{
  "dataset": {
    "type": "svg_dsl",
    "source": "datasets/svg_dsl/v1",
    "inspection": "dataset_visualizer.html"
  }
}
Static CKE binding map showing templates, weights, dimensions, graph streams, lowered IR, and generated C Template + weights binding map

The template is the circuit, the weights are the components, dimensions are the pinout

This is the part that makes the idea concrete. The template says what circuit should exist. The weights manifest says what tensors are available. The dimension map proves the plugs fit. Header/body/footer organizes the circuit into phases. Producer/consumer edges describe which operation creates a stream and which later operation consumes it. Then IR1, the three lowering stages, the memory plan, and generated C make the circuit executable.

Template weights dimensions and IR binding map A granular diagram showing template circuit, weights manifest, dimension mapping, header body footer, producer consumer edges, IR stages, lowering, and generated C. Template Circuit ops + order Weights Manifest tensor names Dimension Map pinout check Bind Slots wq -> q_proj Header Body Footer phase contract Producer Consumer named streams IR1 + Lower 1 / 2 / 3 graph -> calls LoweredIR buffers / dtype kernel ids Generated C runtime calls Rule No generated C before the template, weights, dimensions, and streams agree.

Template circuit

The architecture says what should exist.

The template is the model-family circuit. It declares header/body/footer phases, operation order, attention policy, recurrent paths, bridge rules, and output contract. It does not store weights and it does not execute math.

{
  "template": {
    "sequence": ["decoder"],
    "header": ["tokenizer", "embedding"],
    "body": ["rmsnorm", "qkv_proj", "attn", "mlp"],
    "footer": ["final_norm", "lm_head"]
  }
}

The Actual V8 Artifact Ladder

There are two useful vocabularies in the repository. The template pipeline document explains the design as Template to GraphIR to LoweredIR. The current build_ir_v8.py implementation exposes that design through more granular artifacts. Keeping both views is useful as long as they are not confused.

Concrete v8 artifactWhat becomes explicitTypical failure it can expose
Template JSONFamily flags, contracts, phases, operation order, branches, and graph-slot overrides.The architecture itself is incomplete or ordered incorrectly.
Weights manifestAvailable tensors, shapes, dtypes, quant formats, and file offsets.A required projection or norm tensor is absent or mapped under the wrong semantic name.
IR1Expanded operations, layers, weights, kernel IDs, and producer/consumer dataflow.A template operation was silently dropped or reads from the wrong producer.
IR Lower 1Kernel-map contracts stitched onto the fused IR1 operations.The selected operation and kernel signature disagree.
Memory planTensor lifetimes, persistent regions, scratch reuse, alignment, and arena placement.Two live tensors overlap or a persistent state is treated as temporary scratch.
IR Lower 2Concrete buffer assignments, offsets, dtype views, strides, and pointer expressions.A semantically correct input resolves to the wrong physical arena address.
IR Lower 3Call-ready function names and ordered argument expressions.The right kernel receives arguments in the wrong order or from the wrong source class.
Generated CLoops, calls, constants, runtime state, and instrumentation compiled for Linux CPUs.Code generation fails to preserve the call-ready plan.

This distinction is not cosmetic. It provides a binary-search strategy for debugging. If IR1 already has the wrong producer, rewriting the AVX kernel is wasted effort. If IR1 is correct but IR Lower 2 points at the wrong offset, the bug belongs to lowering or memory binding. If the call-ready artifact is correct but output parity fails, then the kernel, runtime state, tokenizer, or reference contract becomes the next suspect.

Why Graph Slots Matter

In newer v8 hardening work, the template audit is becoming stricter about explicit dataflow. For some operations, it is not enough to say the op exists. CKE needs to know which semantic stream feeds it.

Diagram showing how ordered template operations and explicit graph slots become producer references in CKE IR1
The template does not use a publish/subscribe API. Its ordered operations and optional graph_slots provide enough information for CKE to materialize producer/consumer references in IR1.

Example: a projection such as qkv_proj, mlp_gate_up, or mamba_in_proj must consume the correct normalized input, not accidentally read the residual stream or the embedded input. Those mistakes can compile. They can even produce fluent-looking output. But the circuit is wrong.

{
  "op": "qkv_proj",
  "graph_slots": {
    "inputs": {
      "x": "layer_input"
    }
  }
}

That tiny slot declaration matters. It lets the audit layer ask: does this projection consume layer_input, or did lowering wire it to main_stream by accident? When no override is necessary, the ordered operation sequence supplies the normal chain. When an architecture has branches, persistent state, compressed KV, or multiple semantic streams, explicit slots remove ambiguity.

The Audit Layer Is The Safety Rail

The v8 audit script is deliberately artifact-first. It validates the generated circuit artifacts after IR generation and lowering. If IR1 or LoweredIR is wrong, generated C will be wrong too. The optional generated-C audit is only a final preservation check.

The implementation is in audit_template_circuit_v8.py. It can inspect the template, IR1, lowered IR, and generated C independently. That separation is important because it identifies the first artifact where the circuit diverges instead of treating every mismatch as a kernel bug.

python3 version/v8/scripts/audit_template_circuit_v8.py \
  --template version/v8/templates/qwen35.json \
  --require-explicit-template-edges \
  --ir1 /path/to/ir1_decode.json \
  --lowered /path/to/lowered_decode.json \
  --c-source /path/to/model_v8.c \
  --json-out /path/to/template_circuit_audit.json

The kind of failure it catches is exactly the kind that matters in CKE:

  • a Mamba input projection reading the wrong stream
  • MLA decompression consuming unnormalized compressed KV
  • a partial RoPE concat consuming the wrong query/key pieces
  • a quantized activation bound to a semantic fp32 buffer instead of a physical quantized view
  • generated C using the wrong arena pointer for an operation input

This is why I keep saying support means evidence. A model path is not mature because it compiles. It is mature when the circuit, tensor flow, lowered buffers, generated C, parity, and profile all tell the same story.

Model Families Are Not The Same Circuit

This is also why templates matter more as CKE adds more model families.

Family surfaceTemplate-level differencesWhy CKE cannot hide it
Qwen3-VLMRoPE, QK norm, visual-text prefill policy, image markers, staged decode.The decoder path must understand multimodal bridge rules.
Gemma-style modelsSentencePiece/chat contract, QK norm, softcap/output rules, sliding/full attention policy.Generic LLaMA assumptions can produce wrong output behavior.
GLM-style modelsExplicit projection edges, final logits input, tokenizer/chat rules.The first-token path is sensitive to template and tokenizer drift.
Kimi/MLA/MoE-like pathsCompressed KV, LoRA-style decompression, partial RoPE, routed/shared expert ops.The dataflow is too specific to be guessed safely.
Mamba/recurrent pathsInput projection split, conv1d, dt softplus, selective scan, recurrent state.The residual stream and recurrent stream must stay separated.
Vision encodersPatch/image tokenization, vision embedding, encoder body, projection/footer.The output becomes an embedding boundary for multimodal integration.

This is the practical reason CKE cannot just say "transformer support." Transformer families keep mutating. The runtime needs a way to describe those mutations without hardcoding every branch into C.

Prefill And Decode Come From The Same Circuit

Another important design point: prefill and decode should not become two unrelated templates. The architecture circuit is the same, but the execution plan changes. Prefill may run full prompt attention and build the KV cache. Decode may run one token at a time while reading persistent KV state. The graph contract should be stable enough that CKE can derive both execution modes from the same family description.

That matters for future posts on prefill versus decode, KV-cache layout, and CPU batching. If prefill and decode are described as two disconnected worlds, the runtime becomes harder to reason about. If they are different plans over the same template-defined circuit, the differences become auditable.

What Evidence Should Look Like

For CKE, evidence should move through the stack. A blog post, test result, or benchmark should not merely say "the model runs." It should show the artifacts that make the claim meaningful.

ClaimWeak evidenceBetter CKE evidence
Family support existsA demo prints tokens.Template, model map, IR report, generated C, smoke result.
Architecture is represented correctlyThe op names look familiar.Template audit checks explicit graph slots and critical edges.
Lowering is correctThe file compiles.LoweredIR uses the expected buffers, dtype views, and kernel IDs.
Numerics are correctThe answer looks plausible.Layer/logit parity against PyTorch, llama.cpp, or another reference path.
Performance improvedOne faster wall-clock run.Repeatable tok/s, perf counters, VTune/Advisor, kernel-level explanation.

The result is not a fictional maturity score or a decorative hardening curve. It is a chain of inspectable files and pass/fail gates. Each artifact narrows the remaining uncertainty. That is the practical value of turning the model family into a circuit map.

How To Read This As A Kernel Engineer

A kernel engineer should read the template as a constraint map. What is the input stream? Which tensors are persistent? Which buffers are scratch? Which operation is numerically sensitive? Which op is bandwidth-bound? Which op is compute-bound? Which input must stay fp32 and which one can become a quantized physical view?

The template does not answer every one of those questions, but it makes the questions possible. Once the architecture circuit is explicit, GraphIR can name the tensor flow, LoweredIR can choose concrete kernels, the memory planner can allocate arenas, and profiling can measure the real bottleneck.

This is the difference between a framework user and a runtime engineer. A framework user asks whether the model runs. A runtime engineer asks what path ran, what memory it touched, which kernel dominated, and whether the result matches the reference.

Why This Belongs In The Series

This post sets up the next CKE topics: GraphIR, LoweredIR, the kernel registry, prefill versus decode, memory planning, and numerical parity. Templates are the first public artifact in that chain. If the template is vague, every later stage inherits that vagueness.

So the rule is simple: before CKE claims support for a model family, the circuit map has to be legible. The model should not be a rumor hiding in generated code. It should be a declared architecture contract that can be inspected, audited, lowered, measured, and improved.

Related Notes