Templates Are Circuit Maps: How CKE Describes A Model Family
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.
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.
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.
{
"sequence": ["decoder"],
"body": ["rmsnorm", "qkv_proj", "rope_qk"]
} The Short Version
| Question | Answer | CKE 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.
| Stage | Question it answers | What should stay visible |
|---|---|---|
| Template | What architecture circuit does this model family need? | Sequence, block types, ops, contracts, branch declarations. |
| GraphIR | How do symbolic tensors flow between declared operations? | Inputs, outputs, stable names, branch taps, collect/stitch points. |
| LoweredIR | Which concrete runtime operations and kernels should execute? | Kernel IDs, dtype decisions, physical activation views, buffer slots. |
| Memory planner | Where do tensors live and when can storage be reused? | Persistent buffers, scratch arenas, KV cache, lifetimes. |
| Generated C | What does the runtime actually execute? | Pointer expressions, call order, strides, kernel calls, constants. |
| Parity/profiling | Did 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 job | Owner in CKE | Why the separation matters |
|---|---|---|
| Store weights or offsets | Weights manifest and model maps | The same architecture can have different tensor files and quant formats. |
| Choose final kernels | Kernel registry and selection layer | Kernel choice depends on dtype, shape, ISA, and layout. |
| Define physical memory layout | LoweredIR and memory planner | Architecture order is not the same as arena allocation. |
| Hide model-family branches in code | Template control primitives | Branching should be declared as graph structure, not scattered lowerer logic. |
| Prove correctness | Parity and audit gates | A 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.
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.
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 family | What the circuit must preserve | What CKE should inspect |
|---|---|---|
| Ordinary decoder | Embedding, residual stream, norm, QKV, RoPE, attention, MLP, final logits. | First-token behavior, KV-cache shape, logits input, tokenizer/chat contract. |
| Encoder | Patch/frame/features into dense embeddings with bidirectional context. | Input preprocessing, positional policy, pooled/projected embedding shape. |
| Encoder-decoder | Encoder memory and decoder causal stream as separate memory surfaces. | Cross-attention K/V source, decoder self-attention mask, timestamp/output rules. |
| Interleaved attention | Layer 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 / Mamba | Residual 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 bridge | Vision encoder output, projection, marker insertion, MRoPE, visual-text prefill. | Image token positions, bridge shape, decoder handoff, staged decode. |
| Training DSL | Dataset contract, tokenizer, model spec, loss, evaluator, and artifact lineage. | Data validity, eval definition, model dimensions, generated dataset visualizer, IR report. |
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.
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.
{
"dataset": {
"type": "svg_dsl",
"source": "datasets/svg_dsl/v1",
"inspection": "dataset_visualizer.html"
}
} 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 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 artifact | What becomes explicit | Typical failure it can expose |
|---|---|---|
| Template JSON | Family flags, contracts, phases, operation order, branches, and graph-slot overrides. | The architecture itself is incomplete or ordered incorrectly. |
| Weights manifest | Available tensors, shapes, dtypes, quant formats, and file offsets. | A required projection or norm tensor is absent or mapped under the wrong semantic name. |
| IR1 | Expanded operations, layers, weights, kernel IDs, and producer/consumer dataflow. | A template operation was silently dropped or reads from the wrong producer. |
| IR Lower 1 | Kernel-map contracts stitched onto the fused IR1 operations. | The selected operation and kernel signature disagree. |
| Memory plan | Tensor lifetimes, persistent regions, scratch reuse, alignment, and arena placement. | Two live tensors overlap or a persistent state is treated as temporary scratch. |
| IR Lower 2 | Concrete buffer assignments, offsets, dtype views, strides, and pointer expressions. | A semantically correct input resolves to the wrong physical arena address. |
| IR Lower 3 | Call-ready function names and ordered argument expressions. | The right kernel receives arguments in the wrong order or from the wrong source class. |
| Generated C | Loops, 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.
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 surface | Template-level differences | Why CKE cannot hide it |
|---|---|---|
| Qwen3-VL | MRoPE, QK norm, visual-text prefill policy, image markers, staged decode. | The decoder path must understand multimodal bridge rules. |
| Gemma-style models | SentencePiece/chat contract, QK norm, softcap/output rules, sliding/full attention policy. | Generic LLaMA assumptions can produce wrong output behavior. |
| GLM-style models | Explicit projection edges, final logits input, tokenizer/chat rules. | The first-token path is sensitive to template and tokenizer drift. |
| Kimi/MLA/MoE-like paths | Compressed KV, LoRA-style decompression, partial RoPE, routed/shared expert ops. | The dataflow is too specific to be guessed safely. |
| Mamba/recurrent paths | Input projection split, conv1d, dt softplus, selective scan, recurrent state. | The residual stream and recurrent stream must stay separated. |
| Vision encoders | Patch/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.
| Claim | Weak evidence | Better CKE evidence |
|---|---|---|
| Family support exists | A demo prints tokens. | Template, model map, IR report, generated C, smoke result. |
| Architecture is represented correctly | The op names look familiar. | Template audit checks explicit graph slots and critical edges. |
| Lowering is correct | The file compiles. | LoweredIR uses the expected buffers, dtype views, and kernel IDs. |
| Numerics are correct | The answer looks plausible. | Layer/logit parity against PyTorch, llama.cpp, or another reference path. |
| Performance improved | One 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.