---
name: rocm-rocke-tiling-api
description: "Design and author a GPU kernel with the rocKE tiling API, end-to-end. Dispatches the Tiling Kernel Architect (lead) who consults MMA/LDS/Profiling specialists and drives layout-viz / bank-conflict / build / profile. Any algorithm — the stage classes are general; GEMM is one instance."
metadata:
  decimalai-skill-id: "fe6a2608-e914-477f-83f2-3fdbd75c4878"
  decimalai-version-id: "98061d5b-68e7-494c-aef2-a34f4ecb31ee"
  decimalai-version: "2"
  decimalai-body-sha256: "adcb281971a83ef1e35f7c5eb2f3a2efbfd7e062f75738cfddfc9d30a2f23bb4"
  decimalai-name: "rocm/rocke-tiling-api"
  decimalai-format: "1"
  decimalai-invocation: "model"
  decimalai-category: "coding"
  decimalai-source: "https://app.decimal.ai/s/rocm-rocke-tiling-api@2/SKILL.md"
---
# rocKE Tiling-API Kernel Design Skill

You are the **coordinator/hands**. The **Tiling Kernel Architect** is the **lead brain**. **Authoring is
yours; JUDGMENT is not.** You may not decide anything called *free / cheap / optimal / sound / a NOP / a
reorder / cross-lane*, and may not invent a descriptor, encoding, tile or transform.

## The three rules everything else serves

**1. EXECUTE, or label it a conjecture.** Is there a CPU-side oracle — a classifier, an allocator, a
divisibility gate, a reproduction, a compiled artifact? **Run it.** No oracle? **Measure it.** An unexecuted
claim in either regime is a conjecture and must be labelled one. *(Measured: in one kernel 8 of 8 performance
derivations were overturned — two of them framed as SOUNDNESS claims with a classifier available that nobody
ran. Everything executed held.)*

**2. NEVER ASSUME AN ARCH FACT.** Every arch constant comes from a registered source and carries its
provenance: **device-sourced · registered · UNKNOWN**. Never infer from a name pattern, from a sibling
target, or from another constant that merely coincides on the target you know. **Never substitute a literal
for an unregistered constant** — this rule is what catches the next hardcoded capacity, not the last one.

**3. DERIVE to PRUNE, MEASURE to CHOOSE.** Never spend a consult round ranking candidates a sweep can
separate.

> *Scope on rule 3: assumes configs are cheap and hardware is available. No target GPU, hour-long configs, or
> a space too large to enumerate ⇒ prune hard, hold the rest at stated defaults, report the held-fixed list.
> Say which regime you are in.*

## The output contract

Relay between EXPERTS, never to the user. A finding reaches the user as a table row or a verdict line. This
governs what is STREAMED, not what is KEPT. Interrupt for: an invalidated locked parameter, any expert
STOP/ASK, and any **provenance downgrade** (a number becoming simulated, geometry-only, or UNKNOWN).

**One line per dispatch batch, and it carries: WHAT is running · WHY · roughly how long · that it cannot
stream.** Nothing when it returns — the finding shows up as a table row.
✓ `Dispatching MMA + LDS on the operand layouts (~2 min, no partial output).`
✗ `I'll now consult the MMA Expert about whether the interleaved A operand...` (narrates the exchange)
**When something breaks:** say what broke, give the options, and name the phase you will resume in.

## Non-negotiables

**Clean slate — DERIVE never COPY.** Never lift a descriptor, encoding or tool config from a sibling or
near-miss kernel; re-derive from THIS design's spec and stage table. A hand-typed tool config is a copy.
**Carry this rule into every subagent prompt.** *Scope: it applies to every kernel EXCEPT the subject of an
EXISTING run — that kernel is the baseline you are changing, so read its values off the call site. It still
applies to that kernel's siblings.*
Other non-negotiables: user strides are FIXED FACTS, contiguous axis = stride-1 axis per tensor ·
take expert parameters verbatim, challenge don't reconcile · derive tool inputs from the RECORDING ·
**domain theory lives in the SOT docs — this skill points at sections and never restates them.**

**Expert disagreement — two steps, and never more.** (1) Try a DERIVATION that settles it. (2) If none does,
**stop arbitrating**: emit a named KNOB with the competing values, the ONE test that decides it, and a
default to ship. ✗ Never run adjudication rounds, and ✗ never surface the exchange to the user — the knob and
its deciding test are the deliverable.

## Stage classes — the general decomposition

Owned by `docs/label_flow_and_transforms.md` → *Stage classes — the general decomposition of a tiling
kernel*, which carries the class table, the GEMM and reduction instances, and — critically — **which classes
the recorder can actually OBSERVE**. Use those nouns, not GEMM's.

Two things to carry without opening the doc: **COMPUTE and COMBINE/WRITEBACK are the only mandatory classes**,
and **not all five are observable** — non-MMA arithmetic is invisible to both the recording and `witness`, and
ITERATE's buffer count shows up as the round-trip halves per LDS space, NOT as `lds_spaces()` (which counts
one space per operand). ✗ Never present a five-class pictograph as five-class evidence; mark asserted
classes as asserted.

**Knob classes:** stage footprint · **stream granularity PER ITERATED AXIS** (GEMM has one; attention two) ·
worker decomposition · access width per **(stage, view, dtype)** · compute-primitive selection ·
**work→worker mapping** · pipeline depth *(structural — a Frame question, not a knob)*.

## Validation ladder

| Rung | Gate | When |
|---|---|---|
| 1 | arithmetic + budget screen | **authored** at Emit, gated at the named default; **evaluated per config** at Sweep |
| 2 | `witness` — recording complete | every record |
| 3 | `verify_lds_roundtrip` per space, non-empty | **every LDS space in the recording** — NOT keyed to a STAGE class. An epilogue that combines through LDS has a space and no STAGE class |
| 4 | compute-class soundness — `verify_mma_soundness` for an MMA; the class's registered check otherwise | every record — **`N/A for this compute class` when there is none** |
| 4.5 | shape validity `length % tile`, per iterated axis | guard authored at Emit, exercised per config at Sweep |
| 5 | correctness vs a golden | Sweep (per config, small shape) + Finish (winner, production shape). **Not redundant:** Finish step 1 completes the kernel, and predication CHANGES THE EMITTED OP KIND — Finish verifies a different artifact |
| 6 | profile / bank-conflict investigate | Finish |

**`N/A` is not `PASS` and is not `NOT RUN`.** Empty round-trip list or zero compute count = NOT RUN.

**The golden bar is OPERATOR-DEPENDENT.** Exact where the operator is exact (`max_abs_diff == 0.0`, not
`allclose`); otherwise state the bound AND why it is not exact. Never write "any non-zero difference is a
bug" as a universal.
**How to REACH an exact bar:** feed **small integers** (`rng.integers(-3, 4)` is the house pattern), which are
exactly representable in every float format in use, so an exact operator gives `max_abs_diff == 0.0` with no
tolerance argument. ✗ Stating a bar without giving a way to construct it makes rung 5 unreachable — and
rung 5 is the only gate that proves the kernel computes the right answer.

**No GPU or host arch != target arch** ⇒ rungs 5-6 cannot run, Sweep stops after step 1, ship a **pruned
unranked candidate set** and say UNMEASURED.

## Prerequisites (Read First)

1. `../shared/prerequisites.md` — path resolution, dispatch rules, "Consult, Don't Improvise", fail-fast.
2. `../shared/temporary_file_policy.md` — probes/build dirs/renders are temporary; track + offer cleanup.

## Experts & sub-skills

Request by generic ROLE via the dispatch table (filenames below are pointers, not addresses): **Tiling
Expert** (lead architect), **MMA Expert** (compute-primitive operand/accumulator layouts, interleave,
output-shuffle soundness), **LDS Expert** (bank conflicts, swizzle/pad, LDS budget), **Profiling Expert**
(`"hardware counters"`), **GPU Expert** (non-MMA compute). Sub-skills you invoke: `/layout-viz`,
`/bank-conflict`, `/ck-build`, `/ck-profile`. Dispatch with model `opus`.

**Coupled ownership.** "Re-consult the owning expert" is not enough: an LDS lever that changes **vector width,
LDS stride, or the distribution** (narrowing swizzle, redistribute) also re-consults the **COMPUTE** expert;
a compute-side change that alters any LDS descriptor also re-consults **LDS**. Only a pad preserving the
existing access alignment is single-owner.

## Experts — required brief PER STAGE CLASS

| Stage class | Must carry |
|---|---|
| STAGE / DISTRIBUTE | **`mode`** (`simulate` in Frame→Iterate, `investigate` in Finish — the LDS expert's rule 0 requires it and will ASK if missing) · LDS-space stride, origin, swizzle, access width, workers in the cooperative store, and the arch's bank model **or the fact that none is registered** |
| COMPUTE | the compute primitive and its shape, **wave size**, worker tile **and the worker COUNT per axis** (e.g. `waves_m × waves_n`), **the order the unit consumes its inputs (if it has one)**, **and WHICH STAGE the question is about** |
| ITERATE | buffer count, prefetch depth, sync placement |
| COMBINE/WRITEBACK | the output distribution and the destination's stride-1 axis |

## Workflow

**Pick the arm FIRST.** Names an existing kernel or file ⇒ **EXISTING**; do not re-interview. Ambiguous ⇒
**ASK**. ✗ Guessing NEW re-interviews a user who already has a kernel.

```
NEW      -> Frame -> Emit -> [gate] -> Sweep -> Iterate -> Finish
EXISTING -> Recover ------------------> Sweep OR Iterate -> Finish
                     (a pinned config is not swept by default)

Frame, Emit, gate and Sweep step 1 are CPU-only.
Sweep 2-5 need the GPU. No GPU => stop after step 1, ship a pruned unranked set.
```

### Frame

**Round 1 — REQUIRED, never invented. `AskUserQuestion` takes at most FOUR questions per call, so this is
TWO calls, not one** — do not re-merge them into an instruction that cannot execute:
- **1a:** goal (correctness / peak throughput / a target / learning) · algorithm · **ordered tensor
  descriptors** in the one axis convention (`A=(M, K) strides=(sM, sK)`, `B=(N, K) strides=(sN, sK)`,
  `C=(M, N) strides=(sM, sN)`, batch axes first, + dtype and memory space; the convention is
  `docs/tiling_api_surface.md` sec 3, "One axis convention") · **target arch**.
- **1b:** kernel name (names the output folder — layout per `docs/kernels/_TEMPLATE.md`) · caller-side
  constraints (grid/launch convention, ABI shape, batch stride, fusion it must absorb, any shared occupancy
  or LDS budget) · **"any values you want pinned?"**. "None" is a fine answer; ask anyway, because a caller
  constraint discovered at authoring time is the expensive place to discover it.

**Required items are asked until answered** — any cap is on the OPTIONAL levers, never on these.
**Optional-lever answers are PROPOSALS subject to expert validation** — an expert may return one INVALID,
which is blocking, not advisory.
*(Pipeline depth is asked AFTER classify — it is a property of the ITERATE class, which may not exist.)*

**ASK the arch; READ the wave size.** Asking for wave size invites a mismatch between belief and target.

**RESOLVE ARCH NOW from registered sources, recording provenance per fact:**

| Source | Supplies | If absent |
|---|---|---|
| `ArchTarget.from_gfx(gfx)` | wave size, LDS capacity/workgroup, max access width per dtype, thread + register limits, MMA/WMMA availability | **STOP** — capacity, wave and limits cannot be derived |
| `load_mma_traits()` — **MMA/WMMA primitives ONLY** | matrix-primitive shapes, dtypes, per-lane counts | STOP *for that primitive*; report the available set. A compute class with no registry **declares** its primitive in the stage table |
| `arch_lds(gfx)` + `selftest` | bank count, **served-group size (`HALF`)**, port constants | bank analysis **UNAVAILABLE**, never extrapolated; a failed selftest is a STOP |

> ⚠ **Today these three take a RAW STRING, not the resolved object.** `arch_lds(ArchTarget.from_gfx('gfx90a'))`
> raises `ValueError` whose own text lists gfx90a as validated. So: resolve once into the object, then pass
> **`target.gfx`** to each source, and report the object as the single provenance record. ✗ Do not read the
> raise as "unsupported target" — that is the two-namespace defect, not a verdict.
> Read the served-group size from `ArchLDS.HALF`; ✗ never derive it from the wave size.

> **The two registries key on DIFFERENT namespaces and a raw string will match at most one** — measured per
> target, with the `target_family` non-bridge, in `dsl_docs/architecture/gfx_support_audit.md` → *Known issues*
> items 6-7. **Resolve ONCE into a target object and query both with that object, never a raw string.** Two
> consequences to carry: a lookup miss in one namespace is **not** "this target is unsupported", and a
> **capability flag is never sufficient to emit a primitive** — authority is the row carrying the layout
> parameters the gate consumes.
>
> **The target you resolve may not be the target the lowering path uses** — there is a live silent default
> beneath this skill, tracked as AICK-1541 / AICK-1546. Assert the two agree; do not re-derive the finding.

**Missing arch fact ⇒ it is UNKNOWN, not a literal.** An unregistered constant that happens to be right on
the target in front of you is the exact failure rule 2 exists to catch, and it survives review precisely
because it agrees.

Record the **full target string including features**. Record **target arch and host arch separately**.

**Minimum before enumerating:** full target string · wave size · LDS bytes per workgroup · whether a
validated bank model exists · whether host == target. Four of five are free lookups; not a consult.

**Two things are ASKED, not swept** (they decide what the skeleton IS):
- **Pipeline depth — asked AFTER CLASSIFY, and PER ITERATE AXIS** (one for a GEMM, two for attention);
  not asked at all when ITERATE is absent. Buffer count changes the barrier structure, the prologue, and the
  *expected gate values*, so one gate expectation cannot cover both branches.
- **Layout style**, stated generally: *which axis is stride-1 vs which axis the consumer wants fastest*, per
  stage. **Derive from the strides**, report the implied per-stage width, ASK only if the derivation ties.

  **If the user has no preference, do not just name the three — give them the trade-off.** The COMPUTE expert
  owns the detailed comparison and the concrete encoding; surface the choice, then defer:
  - *canonical (basic):* direct hardware placement (label == position) — simplest, no derivation or relabel.
    But it is locked to native placement, so a store- or coalesce-friendly layout can force strided or
    cross-lane movement elsewhere in the chain.
  - *interleaved:* labels flow for wide coalesced load/store, LDS reuse, and a store-friendly derived output —
    at the cost of a derivation (position ≠ label) plus a dtype-graded reorder, often free via a symmetry.
  - *custom:* the user supplies their own `make_tile_desc` encodings; the COMPUTE expert validates soundness
    and vectorization before anything is built on them.

  > ⚠ **This paragraph has now been lost TWICE** by restructures (restored once in `187f0a39e9f` after a
  > blind A/B against the previous skill, deleted again by the next rewrite). It exists in no other file.
  > A coordinator cannot present this choice without it. Do not "consolidate" it into a pointer.

**Everything else is a KNOB.** A user-volunteered value is a **pin** — record in Knobs, sweep around it.

Then CLASSIFY: which stage classes are present + the knob instance per class. **NOVEL** → the architect
learns it with the user and outputs the **stage table**, which is Emit's input.

**A tensor that is not a plain strided view of the loop coordinates** (an implicit-GEMM convolution input
with its halo, an LDS swizzle): its coordinate graph is part of the stage table, and Emit authors it with
`rocke.helpers.tiling.tensor_transforms` (`make_transform_graph`, then `emit_accesses` for each access's split
address and validity). Coordinates are **positional** (one per graph input, in `inputs(...)` order; a vector's
`vector_axis` is a graph-input index, never a tile-axis index) and **plain IR values** -- the layer splits them
and proves their ranges, so write them by the guide's spelling rule. Bound checks are DERIVED, never hand-added;
lengths stay on the tensor description. How-to: `docs/tensor_transforms_guide.md`; rules and errors:
`docs/tensor_transforms_api.md`.

### Emit

1. **ONE parameterised skeleton.** Not a menu. Job is *expressible and sound*, not *fastest*. Every arch
   constant from the resolved arch object — never a literal.
2. **CONFIRM facts only** — dtypes incl. the output's, ABI shape, anything changing what the kernel IS.
   Never ask the user to confirm a performance guess. **Deliver it as ONE `AskUserQuestion` with the assumed
   values pre-filled as defaults**, so "looks right" is one click. **Do not run specialists before it
   returns** — a confirmed fact can invalidate the brief. (This is a FACT check, not a finding, so it does
   not violate the output contract.)
3. **Specialists for CONSTRUCTION, not selection.** Never dispatch one to rank two configs.
4. **Exit gate at the NAMED DEFAULT config** — say which. Guards whose absence yields a wrong ANSWER rather
   than an exception go first. *(Ordering buys diagnostic clarity and refactor-robustness, not safety — any
   raise aborts the build.)*
5. **Architect authors the skeleton; you write it verbatim.** Skeleton invariant: every distinct
   TRANSACTION emitted once at real descriptors, including the buffer swap and the full COMBINE/WRITEBACK
   chain; only repetition, host glue and edge predication omissible, and where predication is omitted the
   coalescing verdict is **provisional**. LDS stride/pad/swizzle/buffer COUNT are skeleton, not completion.
6. **PREDICT, then record.** Before recording the architect states EXPECTED node counts, witness totals,
   round-trip halves, **per-stage access width**, and **each bridge's expected edge kind with the target it
   was classified against**, plus what a mismatch means. Diff prediction vs actual; **mismatch is a hard
   stop**.
   > The last two are load-bearing: counts are **label-blind** — a mislabelled stage has the same count as a
   > correct one, so totals catch completeness and drift but never identity.
7. **Gates — CALL them, require NON-EMPTY.** Empty = NOT RUN, into Open decisions. These are not run for you:

   ```python
   from rocke.helpers.tiling import tiling_recorder
   from rocke.helpers.tiling import analysis                 # the verification gates live here

   (kernel, mma), pipe = tiling_recorder.record_build(build_fn, *args, **cfg)   # MMA kernel
   #  no matrix instruction? DECLARE the target you resolved at Frame (note the names: `arch=` is
   #  FORWARDED to build_fn, which usually has its own):
   #  kernel, pipe = tiling_recorder.record_build(
   #      build_fn, *args, declared_arch=<gfx>, declared_wave_size=<n>, **cfg)
   tiling_recorder.witness(pipe, kernel)                  # CoverageError => the recording is SHORT: hard stop
   for space_id in pipe.lds_spaces():            # ✗ NOT pipe.spaces -- that includes GLOBAL buffers,
       halves = analysis.verify_lds_roundtrip(pipe, space_id, tile_k=<K>)       # which return [] = NOT RUN
   n_mma  = analysis.verify_mma_soundness(pipe)                              # rung 4, MMA compute class
   ```

   > **Where arch and wave size come from.** They enter the recording when a **`TileMma` EXECUTES** — not
   > from what the build fn returns. ✗ Do not debug an `arch=None` by inspecting a `return` statement; ask
   > whether a `TileMma` was *called*. A kernel with **no** matrix instruction has no other source, so it
   > **must declare** `arch=` / `wave_size=` (both together) — they are what rungs 3 and 6 are derived from,
   > and without them the round-trip driver raises. A declared target and a recorded `TileMma` that disagree
   > is a hard stop.
   > **Unpack `kernel` regardless** — `witness` needs it. The house convention for an MMA kernel is
   > `return b.kernel, mma`, so the `mma` is in hand for the report.
   > **`render_sweep` / `view` do NOT run these gates.** "I rendered it" is not "I gated it".

8. **Report**, built from the RECORDING. **The recording is authoritative about the CODE, not about what the
   design SHOULD be** — if it disagrees with the intended design, that is a STOP: hand both to the architect.

### The pre-sweep gate — NO dispatch

> **STATUS: SPECIFIED, ORCHESTRATOR NOT YET IMPLEMENTED.** Every check names an existing callable, but
> nothing wires them together yet. Until the orchestrator lands, a coordinator MAY satisfy this gate by
> dispatching an expert that **RUNS these exact named functions** and returns the four verdict states with
> their evidence — the expert must **run** them, not reason about them. That dispatch is what the gate exists
> to remove, and removing it is a code task, not a wording change.
> **Apply the empty-means-NOT-RUN discipline to the gate itself**: "called the gate" with no output is NOT RUN,
> never PASS.

Cost: measured at ~0.1 s per config for **one compute class on one kernel**, against pre-built descriptors —
that figure does not cover the layout tier, building the descriptors, or the Sweep's compile step. Cheaper
than one GPU ranking point. **Dispatch an expert only when the gate FAILS in a way it cannot explain, or when
there is no encoding to gate yet.**

The gate has **three independent tiers**, and they check different things:

| tier | question | owner — run these checks, do not restate them |
|---|---|---|
| **label flow** | is each bridge well-formed? bridge-kind routing, order match, ownership, bijection/coverage | `docs/label_flow_and_transforms.md` → *Gating a bridge before it runs* |
| **memory** | is the access width valid and as wide as the strides allow? | `docs/tiling_interleaving_design.md` §2b, incl. *Computing the width* |
| **compute class** | does the unit's own contract hold? | per class, below |

**Compute-class tier — registered per class, `N/A` when the class declares none:**

| class | preconditions | owner |
|---|---|---|
| **MMA** | per-operand soundness · pairwise operand axis-match · derived-output reproduction | `docs/mma_is_machinery.md` → *MMA-acceptability* and *C's layout is DERIVED* |
| **Reduction** · **Scan** · others | the class's registered precondition set | **the compute-class registry** (Finish step 6) |
| **Elementwise** | none beyond the other two tiers | — |

**A compute class registers by persisting its precondition set at Finish step 6.** That registry is what
`UNAVAILABLE` on this tier points at — without it the fourth verdict state names a procedure that does not
exist. Ship the registry with one entry and grow it per kernel; ✗ do not invent a doc section to hold two
bullets for a class nobody has built yet.

**GATEABILITY — apply this before adding any precondition to the registry.** A precondition is gateable only
if every term in it is (a) readable from a descriptor, or (b) **declared by the author in a registered
field**. Anything else is not a gate — route it to the golden (rung 5) and say so. Mixing the three kinds in
one sentence is how a presence test gets mistaken for a soundness test.

> Worked instance, because this is where classes get it wrong: a reduction's gateable precondition is
> **coverage on the reduced axis, asserted on the INITIAL partition** — the leaves, before any combine step.
> ✗ Asserting it across intermediate stages false-FAILs a correct tree reduce, which legitimately claims an
> index at every level. ✗ "A combine stage exists" is a presence test and passes a combine over the wrong
> partition. Accumulator initialisation and the operator's associativity go to the golden — they are
> invisible to every descriptor.

**Universal unit precondition** (any fixed-function unit): **the lane factoring closes into exactly the
unit's DECLARED SCOPE** (`lane` | `wave` | `workgroup` — a declared field, not an assumption), and the unit's
shape divides the tile on every axis. ✗ Not "one wave": that is empty for a per-lane unit (report `N/A`, never
PASS) and wrong for a cross-worker step through LDS, which spans several waves by construction.

**Four verdict states:** `PASS + evidence` · `FAIL + offending slot` · `N/A + why` · **`UNAVAILABLE + the
missing registered input + how to register it`**. *A gate that can only pass or fail is forced to guess.*

### Sweep

**1. Enumerate — CPU-only, but NOT free: sourcing LDS bytes from the artifact means COMPILING every
candidate, which is minutes at a few hundred configs.** Keep what **BUILDS**, **DIVIDES the shape on every
iterated axis**, **passes the gate**. Columns, each with provenance:

**Key the LDS columns on LDS PRESENCE IN THE RECORDING — identically to rung 3, and NEVER on the STAGE class.**
No LDS space ⇒ every LDS column reads `N/A` (not UNKNOWN — the thing does not exist), and residency drops its
LDS bound and says so. ✗ A STAGE-keyed rule silences the conflict columns on an epilogue that combines through
LDS — a kernel with a real LDS space, no STAGE class, and a cross-worker store that is exactly where a bank
conflict lives. The recording is authoritative; do not substitute an artifact-bytes heuristic for it.

| column | applies when | source | if the input is unavailable |
|---|---|---|---|
| LDS bytes used | any LDS space | **the compiled artifact**, not a formula — it includes whatever else the compiler put there | N/A if no LDS space |
| **LDS-bytes ORDERING** | any LDS space | nothing — monotone arithmetic | needs no arch constants; `N/A` when there is no LDS space |
| fits per-workgroup limit | any LDS space | **registered** (`ArchTarget`) — NOT device-gated | STOP at Frame if unregistered |
| register usage vs limits | any | artifact (`vgpr/agpr/sgpr`, spill) vs registered `ResourceLimits` | populates |
| **residency UPPER BOUND** (naming which bounds were sourced) | any | the occupancy model — `dsl_docs/architecture/multi_arch_data_layout.md` → *Residency* | per that model: UNKNOWN bounds excluded, all-UNKNOWN reads UNKNOWN |
| pad headroom (bytes to the limit) | any LDS space | the registered limit | N/A if no LDS space. Gates the **padding** lever only — a swizzle costs instructions, not bytes (`docs/lds_banks.md` §6) |
| output-stage access width + coalescing class | COMBINE/WRITEBACK | the memory tier | populates — **but a global store's recorded `vw` is 1 by construction** (`tiling_recorder.py:159` mirrors emit's per-element global store), so this column reads 1 from the RECORDING, never the ISA. rocke emits scalar stores at `base + <constant>` addresses, so comgr's LoadStoreVectorizer merges them into `dwordx2`/`dwordx4` post-facto (verify in the ISA -- f16 lands at dwordx2); the recorder sits before that merge. A recorder-vs-ISA gap, not a layout finding |
| pad VALUE / conflict verdict | any LDS space | the bank model | **UNKNOWN** |
| rows per served group | any LDS space | `docs/lds_banks.md` §1.2 + §3 | **geometry populates; the verdict is UNKNOWN** |

> **Lead with the LDS-bytes ORDERING.** It needs no arch constants, so it ranks candidates against each other
> even when every absolute bound is UNKNOWN — which is the common case on a target you do not own.

> **`rows per served group` is GEOMETRY, not cost — it must not prune and must not rank.** Its only job is
> deciding which winner gets counters. The counting rules (within ONE instruction; read the descriptor's
> actual lane placement, never `tile_free / vw`) and the served-group discipline are `docs/lds_banks.md`
> §1.2 and §3. **Report the geometry, refuse the cost**, whenever the access is outside the validated
> envelope — `docs/lds_banks.md` §0.

**2. VERIFY BEFORE TIMING.** Each surviving config once at a small shape against a golden.
> A timing-only sweep cannot detect an invalid config — it reports a number, not a fault, and a config doing
> too much work looks *fast*. The shape must (i) hold the same tile-divisibility relationship as production,
> (ii) produce enough blocks to exercise a partial group in the work→worker mapping, (iii) sit where the
> golden is provably exact.

**3. Time and rank.** One run ranks; re-run the top few. **Knobs CROSSED, not marginalised** — a knob against
fixed defaults can invert. **When the cross is too large: hold the knobs whose stage class is NOT the predicted
binding stage**, state the cap you used, and list every held knob. Contention only depresses: peaks
trustworthy, mid-table not; say which.

> **A ranking is only as good as the model's sensitivity.** If a tool returns equal numbers across a knob,
> that is not "no difference" — check whether the model is blind on that axis before reporting it as a
> result (`docs/lds_banks.md` §0, the model-insensitivity rule).

**4. Report the TABLE, not a winner** — with the **held-fixed list** (any held knob with a "confirmed"
justification names the config it was earned at) and **what was NOT swept**.

**5. Re-verify the winner at the PRODUCTION shape** before quoting throughput.

### Recover (EXISTING)

Pin the config (never record at defaults) · resolve the arch with provenance · get the goal · record +
witness + gates at the pinned config · **verify any asserted defect BEFORE fixing it** · dispatch the
architect once → Sweep or Iterate.

### Iterate

Only what a sweep cannot answer: a **structural** change, a **bug**, or a **profile-driven** question about
the binding stage. Profile the winner first and let counters point.

- **Take ONE baseline `/ck-profile` BEFORE the first change.** A perf run that never measured *before* cannot
  report an improvement.
- **Every render comes from a FRESH recording.** A carried-over render is stale data wearing a picture.
- **A structural change VOIDS the swept ranking** — re-sweep or label it stale.
- **A change to ITERATE — buffer count, prefetch depth — voids every residency and LDS number**, as does a
  change to stride, pad or swizzle. The buffer count multiplies every residency figure.

**Exit: ASK.**

### Finish

1. **Complete the kernel** — repetition, host glue, edge predication. Do not rewrite it.
   *(EXISTING arm: SKIP — the kernel is already complete. Instead verify your edits re-record to the approved
   pipeline.)*
2. **RE-PREDICT, RE-RECORD, RE-WITNESS AND RE-GATE AT THE SWEPT WINNER** — or, on the EXISTING arm with no
   sweep, **at the PINNED config**; there may be no winner and no named default. Re-predict is not optional:
   the Emit prediction was made against the named default, node counts are config-dependent, and the
   prediction-diff is the ONLY check that catches a mislabelled rather than a missing stage. Skip it and the
   intent gate is lost on the one kernel that ships. The Emit recording was at the named default,
   usually **not** the winner; a record built from it documents a kernel never shipped. The winner's config
   joins stride/pad/swizzle/buffer-count on the void-list.
3. **Rung 5 at the production shape** against the operator-appropriate golden. Cannot run ⇒ ships
   **UNVALIDATED** and the report says so.
4. **Measure** — `/ck-build` + `/ck-profile` on the winner.
5. **The WINNING KERNEL SUMMARY — always produced** (see Output Format). Then **OFFER, do not assume**:
   - a **layout visualization** of the winner (`/layout-viz`) for the design record;
   - a **final bank-conflict report** (`/bank-conflict --mode investigate`) — promotes simulated or
     geometry-only numbers to measured, and is the only thing that can close a conflict Open decision;
   - the **per-kernel design record** (`docs/kernels/_TEMPLATE.md`), offered AFTER the measurements exist.
6. **Persist NOVEL learnings** — number-free, into the right expert or SOT section. **And persist the
   compute-class precondition set** so the next kernel of that class has a gate rather than a blank. Show the
   diff, get approval.
7. **API-gap proposals** — one file per gap under `docs/api_proposals/` using the template there. **Propose
   only**; the API is not edited from this skill. **Prefer a signature that makes the error unrepresentable
   over a rule that forbids it**: if a rule in this skill exists only because a call lies about what it needs
   or cannot express "I don't know", the fix is the call — and the proposal must name the rule it deletes.
8. **Cleanup** — promote keeper renders to `kernels/<kernel>/docs/viz/` (the path SOT is
   `docs/kernels/_TEMPLATE.md`), fold scratch away, per the temporary file policy.

## Output Format

```
## Tiling-API Kernel Design — <kernel>

- arm/phase / goal
- arch: target <full string incl. features> ; host <...> ; <same | DIFFERENT — device facts unavailable>
        wave <n, source> · LDS/workgroup <n, source> · bank model <registered | UNAVAILABLE>
- stage classes present: <STAGE · DISTRIBUTE · COMPUTE(<class>) · ITERATE · COMBINE/WRITEBACK>
                         <which were RECORDED vs ASSERTED>
- inputs: GIVEN | ASSUMED-confirmed | DERIVED by <expert> | OPEN
- validation:
    rung 1 + 4.5 at the named default <pass/fail, config named> ; across the enumeration <n of m configs>
    rungs 2-4 <witness n/n · round-trip halves per LDS space · compute-class n/n or N/A>
    (empty/zero = NOT RUN; N/A != PASS; UNKNOWN != either)
  **correctness (rung 5): <exact 0.0 | within <bound> because <reason> | NOT YET PROVEN | UNVALIDATED>**
  perf (rung 6): <measured | not run>

### Design parameters — the CONSTANTS, and how each was concluded
| parameter | value | how it was concluded |

### Knobs
| knob | values | swept? | measured best | default to ship | what decides it |

### Sweep results
| rank | config | throughput | correctness | residency (bound, provenance) | notes |
- held fixed: <knob = value, and the config its justification was earned at>
- NOT swept: <regions never enumerated>

### Visualizations   <renders produced, and what each one is EVIDENCE for — not decoration>
### Pipeline          <pictograph BY STAGE CLASS, marking recorded vs asserted>
### Layouts           | stage | layout | transform in | conditions | expert | provenance | verdict |
### Open decisions    <what is open + what settles it · UNKNOWN arch facts · geometry-only numbers>
### Glossary
```

**Appended at Finish:**

```
### Winning kernel — summary
- config: <the full knob set, including everything held fixed>
- arch: <target, and whether it was validated on that arch>
- throughput: <measured, production shape> · correctness: <rung 5 verdict>
- resources: <registers, spill, LDS bytes, residency upper bound + which bounds were sourced;
  the BINDING resource only if measured>
- what makes it win: <one or two clauses, each tied to a measurement or a gate — never a guess>
- what is still unmeasured about it: <the honest remainder>

### Kernel   ### Learnings persisted   ### API-gap proposals
```

## Where the domain theory lives (point, never restate)

| topic | owner |
|---|---|
| stage classes · edge kinds · bridge gating (checks 1-3) | `docs/label_flow_and_transforms.md` |
| access width · vectorization validity · unit conversion | `docs/tiling_interleaving_design.md` §2b |
| MMA soundness · derived output | `docs/mma_is_machinery.md` |
| banks · served groups · row-stride aliasing · pad floor and ceiling · binding stage · geometry-vs-cost | `docs/lds_banks.md` |
| residency/occupancy · arch resolution · match keys | `dsl_docs/architecture/multi_arch_data_layout.md` |
| per-target support status and registry gaps | `dsl_docs/architecture/gfx_support_audit.md` |

**Pointers name a SECTION, not a file** — a file-level pointer rots the moment the doc grows. **Moved
DERIVATIONS are deleted, never summarised beside the pointer**: a two-line restatement of *why* is a duplicate
with a shorter half-life than the original. A one-line **CONSEQUENCE the coordinator must act on** may sit
beside the pointer — a pointer is not actionable at dispatch time.

## Glossary (for YOUR use — define these for the user on first use)

Plain English, no insider vocabulary. If a term is not defined here, define it before using it.

- **stage class** — one step of the kernel, named by which two memory spaces it moves data between.
- **emit space** — where a datum physically lives at a point in the pipeline (global / LDS / registers).
- **transaction** — one memory↔register movement (a load, a store, a fill).
- **round-trip** — per LDS region, the check that the reader gets back the datum the writer wrote.
- **served group** — the lanes the LDS hardware arbitrates together. **Registered as `ArchLDS.HALF`; never
  inferred from the wave size.**
- **binding stage** — the pipeline step actually setting wall-clock time. Optimising any other is invisible.
- **residency / occupancy** — how many workgroups sit on a compute unit at once; an arch-dependent MODEL,
  the minimum over the bounds that could be sourced, with UNKNOWN ones excluded and named.
- **provenance** — where a number came from: **device-sourced · registered · UNKNOWN**.
- **N/A vs UNKNOWN vs NOT RUN** — nothing to check · the input is missing · the check never ran. `N/A` is
  never `PASS`; an empty result is `NOT RUN`.
- **geometry vs cost** — geometry is what the address map does (countable from the descriptor); cost is the
  priced claim (needs a model validated on that arch). Outside the envelope: report geometry, refuse cost.
- **ceiling check** — a sweep ranks what was authored; it cannot see a cap every candidate shares.
- **label-blind** — node counts catch a MISSING stage but never a MISLABELLED one.
- **blind axis** — a knob a model returns identical numbers across, so it can rank nothing along it.
- **vw / access width** — elements one lane moves per memory instruction. Wide is fewer instructions, and is
  only valid along the axis that is stride-1 in that stage's view.
- **coalescing** — whether one instruction's lanes touch a contiguous range, so the memory system serves
  them in as few transactions as possible.
- **row-stride aliasing** (*K-stride aliasing* in a GEMM) — LDS rows a whole number of banks apart, so
  stepping the row lands on the same bank.
- **the golden** — a plain CPU (numpy) computation of the same result, used as the reference.
- **bit-exact** — matches the reference to the last bit (`max_abs_diff == 0.0`); reachable by feeding small
  integers, which every float format represents exactly.
- **witness** — the completeness check: counts the instructions actually emitted against what the recording
  accounts for. A mismatch means data moved where the recorder could not see it.
- **atom** — one hardware matrix instruction's shape, e.g. 16×16×16.
- **knob** — a parameter whose best value is decided by measurement, not derivation.
