# Aiter Config Shape

> How to add/upload tuned config CSVs under aiter/configs (incl. model_configs/) without introducing duplicate shapes, and how to find & resolve duplicate-shape collisions. Use whenever adding a model's tuned config, merging/uploading config CSVs, editing anything under aiter/configs/**, or when a run hits "duplicate shape entries during merge".

- Skill: `rocm/aiter-config-shape` (Agent Skill)
- Install (CLI): `npx skillmds@latest add rocm/aiter-config-shape`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rocm/aiter-config-shape/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: rocm (https://skillmd.com/u/rocm)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/rocm/aiter-config-shape

---


# aiter config shape-collision standard

Tuned config CSVs in `aiter/configs/` are **not read one file at a time**. At
runtime `aiter/jit/core.py::AITER_CONFIGS.get_config_file` merges, per family,
the canonical `aiter/configs/<name>.csv` **plus every**
`aiter/configs/model_configs/*<name>*.csv`, and `update_config_files`
de-duplicates that merge on a key derived from the matching **untuned** file's
columns (+ `cu_num`/`gfx`/`_tag`). If two rows across the merged files share that
key, the merge **raises** `RuntimeError: Found N duplicate shape entries during
merge of '<family>'`.

Consequence: a per-model config you add can be perfectly fine alone yet collide
with a *different* model's config once both are on `main`. Single-PR CI only
merges your file with current `main`, so two PRs that each add the same shape to
different model files both pass, then break `main` after both land
(**cross-PR / merge-skew hazard**). Reviewers routinely forget this.

Follow this whenever you add "tuned configs for model Y", upload/merge config
CSVs, or touch anything under `aiter/configs/**`.

## The hard rules

1. **A key is (untuned columns) + `cu_num`, plus `gfx`/`_tag` when those columns
   are present.** Never hand-pick key columns — they come from the family's
   `*_untuned_*.csv` header, exactly as runtime derives them (`update_config_files`
   appends `cu_num` if absent, `gfx` only when a `gfx` column exists in the merge,
   and `_tag` only when a `_tag` column exists). Do not assume `M,N,K` alone: e.g.
   `a8w8` also keys on
   `q_dtype_w`, `bf16` on `bias,dtype,outdtype,scaleAB,bpreshuffle`, `fmoe` on the
   full `token,model_dim,inter_dim,expert,topk,act_type,dtype,q_dtype_*,q_type,
   use_g1u1,doweight_stage1` (+ `_tag`).
2. **Your file is merged with the canonical file AND every other model file of
   the same family.** Before uploading, check the merge, not just your file in
   isolation. The family is decided by the substring in the filename
   (`*<tuned_file_name>*`), e.g. `dsv4_bf16_tuned_gemm.csv` joins the
   `bf16_tuned_gemm` family.
3. **One shape, one row across the whole family.** If the shape already exists
   (in the canonical file or another model's file), do **not** re-add it. If yours
   is genuinely faster, replace the existing row rather than adding a second.
4. **Run the collision guard and make it pass before you push.** (below)
5. **Resolve, never suppress.** Fix by removing the redundant row (keeping the
   lowest-`us` winner) — do not widen keys, rename shapes, or delete the guard.
6. **`gfx` matters.** Archs that share a `cu_num` (e.g. gfx950 vs gfx1250 both
   report 256) are only distinguishable by `gfx`. Keep the real `gfx` the tuner
   wrote; don't drop the column. Legacy files without `gfx` are backfilled from
   `cu_num` (256→gfx950, 80/304→gfx942) — do not rely on that for new archs.

## Detect: run the guard

The authoritative check drives the **real runtime merge** (no re-implemented
logic, so it can't drift) against a temp copy of `aiter/configs/`:

```bash
python3 -m unittest op_tests.tuning_tests.test_config_shape_collision -v
```

- Requires torch (importing `aiter` pulls it in); it skips cleanly where torch is
  absent, so run it in an env/container that has torch.
- A failing family surfaces the runtime `RuntimeError`: a `Found N duplicate shape
  entries during merge of '<family>'` message, the colliding rows as a
  `Duplicate rows:` table (the merged config columns — no `_src`/key line is
  printed), and an `Updated files:` list of the source CSVs that were rewritten.

To see it the way production does (runtime prints the colliding rows), trigger
the merge directly on a scratch copy:

```python
import shutil, tempfile, aiter.jit.core as core
tmp = tempfile.mkdtemp(); shutil.copytree("aiter/configs", f"{tmp}/aiter/configs")
core.AITER_ROOT_DIR = tmp
type(core.AITER_CONFIGS).get_config_file.cache_clear()
core.AITER_CONFIGS.get_config_file(
    "AITER_CONFIG_GEMM_BF16", f"{tmp}/aiter/configs/bf16_tuned_gemm.csv", "bf16_tuned_gemm"
)  # raises RuntimeError listing duplicate rows + which files
```

## Find which rows/files collide

The `RuntimeError` (and the test failure) already prints every duplicate row and
its source file. To locate them yourself for a family, merge its files and group
by the runtime key (read the key from the untuned header — don't invent it):

```python
import glob, os, pandas as pd
name = "bf16_tuned_gemm"                      # the family
untuned = f"aiter/configs/{name.replace('tuned','untuned')}.csv"
key = pd.read_csv(untuned, nrows=0).columns.str.strip().tolist() + ["cu_num", "gfx"]
files = [f"aiter/configs/{name}.csv"] + [
    p for p in glob.glob(f"aiter/configs/model_configs/*{name}*.csv")
    if "untuned" not in os.path.basename(p)
]
df = pd.concat([pd.read_csv(f).assign(_src=os.path.basename(f)) for f in files if os.path.exists(f)])
key = [k for k in key if k in df.columns]
print(df[df.duplicated(key, keep=False)].sort_values(key)[key + ["us", "_src"]].to_string(index=False))
```

## Resolve: the auto-dedup already exists — just trigger it on the real tree

**Do not write a dedup script.** `update_config_files` (`aiter/jit/core.py`)
already resolves collisions: for each duplicate key it keeps the lowest-`us` row,
**writes the pruned CSVs back to the source files**, and raises asking you to
re-run. Your job is only to trigger that write-back against the *real* tree and
commit the result.

- **Trigger it (one command):** run the guard's `--fix` mode. Unlike the default
  detection (which runs on a temp copy to stay read-only), `--fix` runs on the
  **real** checkout so `update_config_files`' write-back lands where you can
  commit it:

  ```bash
  python3 op_tests/tuning_tests/test_config_shape_collision.py --fix
  ```

  It resolves every family, keeps the lowest-`us` row per shape, rewrites the
  source CSVs, and prints which files/rows changed. (Equivalent to running the
  model/op/tuner once, or calling `core.AITER_CONFIGS.get_config_file(...)` on the
  real tree — same built-in auto-dedup, no extra logic.)
- **Commit** the rewritten config CSVs (`git diff` shows the pruned rows), then
  re-run without `--fix` to confirm clean.
- **Manual alternative:** if you'd rather edit by hand, from the duplicate list
  delete the slower-`us` copy — usually the shape already exists in the canonical
  or an older model file, so remove it from the file you are adding. Keep exactly
  one row per key (the lowest `us`).

After resolving, re-run the guard until it passes.

## Cross-PR awareness (for authors and reviewers)

- Your green CI does **not** prove `main` stays green — it never saw the other
  in-flight config PRs. When adding shapes shared across models (common decode
  shapes, MoE token grids), assume another PR may add the same shape.
- Backstop (once wired): if this guard is added to a **push-to-`main`** job, two
  PRs that race and both land a colliding shape turn `main` red immediately — fix
  forward by pruning the duplicate (lowest-`us` wins). The guard is not wired into
  CI yet, so today this is a manual/local check, not an automatic backstop.
- Reviewing a config PR: skim other open PRs touching the same family, and trust
  the guard rather than eyeballing shapes.

## References (single source of truth — keep in sync)

- Guard test: `op_tests/tuning_tests/test_config_shape_collision.py` (drives the
  real merge; add a family here if you register a new `AITER_CONFIG_*` file).
- Runtime merge + dedup: `aiter/jit/core.py::get_config_file` /
  `update_config_files` (key derivation, gfx backfill, `_tag`, write-back).
- GPU end-to-end counterpart: `op_tests/tuning_tests/test_run_config.py` (runs
  every merged shape).

## Anti-patterns

- ❌ Checking only your new file, not the family-wide merge.
- ❌ Hand-picking key columns (missing `gfx`, adding non-key `libtype`) instead of
  reading the untuned header.
- ❌ Adding a second row for a shape that already exists somewhere in the family.
- ❌ "Fixing" a collision by widening the key, renaming a shape, or disabling the
  guard.
- ❌ Assuming green PR CI means `main` is safe (ignores concurrent config PRs).
- ❌ Dropping/zeroing the `gfx` column so shapes look distinct.
- ❌ Re-implementing the merge/dedup in a new script — call the runtime or the
  guard test instead.

