Skip to content

Commit f135faf

Browse files
authored
Add AI agent guidance docs (AGENTS.md, CLAUDE.md, skill.md) (#1013)
1 parent 4942391 commit f135faf

3 files changed

Lines changed: 1457 additions & 0 deletions

File tree

.claude/skill.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# skill.md — Claude working guide for mlperf-automations
2+
3+
Compact task-oriented reference. Read AGENTS.md for full technical detail.
4+
5+
---
6+
7+
## Mental model (read this first)
8+
9+
```
10+
mlcflow CLI → finds script by tags → resolves variations & deps →
11+
customise.py:preprocess() → run.sh → customise.py:postprocess() →
12+
caches new_env to ~/MLC/repos/local/cache/{uid}/
13+
```
14+
15+
**Two repos, two roles:**
16+
- `mlperf-automations` (this repo) = content: 377 script directories + `automation/` engine
17+
- `mlcflow` = driver CLI: installs via pip, dynamically loads the engine above
18+
19+
**Three key files per script:**
20+
- `meta.yaml` — identity, tags, deps, variations, env mapping (schema in `automation/script/meta_schema.py`)
21+
- `customize.py` — Python hooks: `preprocess()` builds the shell command; `postprocess()` reads results
22+
- `run.sh` — executes `eval "${MLC_MLPERF_ENDPOINT_CMD}"` and exits non-zero on failure
23+
24+
---
25+
26+
## Task playbooks
27+
28+
### "I need to understand what a script does"
29+
30+
```bash
31+
cat script/<alias>/meta.yaml # tags, deps, variations, input_mapping
32+
cat script/<alias>/customize.py # what it validates and what command it builds
33+
cat script/<alias>/run.sh # what actually executes
34+
cat script/<alias>/README.md # human summary
35+
```
36+
37+
Key fields to read in meta.yaml: `tags`, `deps`, `variations`, `input_mapping`,
38+
`new_env_keys`, `default_env`.
39+
40+
### "I need to add a new script"
41+
42+
1. Scaffold with `mlc add script`:
43+
```bash
44+
# Basic skeleton (copies template,generic)
45+
mlc add script mlcommons@mlperf-automations:<alias> --tags=<tags>
46+
47+
# Copy nearest existing script as the template instead
48+
mlc add script mlcommons@mlperf-automations:<alias> --tags=<tags> \
49+
--template_tags=app,mlperf,inference,reference
50+
```
51+
This creates `script/<alias>/` with `meta.yaml`, `customize.py`, and `run.sh`.
52+
If multiple scripts match `--template_tags`, it prompts you to pick one.
53+
54+
2. Edit `meta.yaml` — update at minimum:
55+
- `alias`, `uid` (already generated), `tags`, `category`
56+
- `input_mapping` (CLI args → env vars)
57+
- `new_env_keys` (what this script promises to export)
58+
- `deps` chain
59+
3. Edit `customize.py` — implement `preprocess(i)`:
60+
- Guard required env vars (return `{'return':1,'error':'...'}` if missing)
61+
- Build the shell command string in `env['MY_CMD']`
62+
4. Edit `run.sh` — ensure it evals the command and exits non-zero on failure.
63+
5. `mlc lint script --tags=<alias>` — fix key order
64+
6. `mlct <alias>` — run built-in tests
65+
7. PR → `main` branch
66+
67+
### "I need to add a variation"
68+
69+
Add to `meta.yaml variations:`:
70+
```yaml
71+
variations:
72+
my-variant:
73+
group: my-group # omit group if stackable (free-standing)
74+
default: true # omit if not the default
75+
env:
76+
MY_ENV_VAR: value
77+
deps:
78+
- tags: get,extra,dep # extra dep only when this variation is active
79+
```
80+
81+
Invoke: `mlcr <tags>,_my-variant` (underscore prefix on CLI).
82+
83+
### "I need to add a conditional dependency"
84+
85+
```yaml
86+
deps:
87+
- tags: get,cuda
88+
enable_if_env:
89+
MLC_MLPERF_DEVICE: [gpu, cuda] # only run if device is gpu or cuda
90+
- tags: get,rocm
91+
skip_if_env:
92+
MLC_HOST_OS_TYPE: [windows] # skip on Windows
93+
```
94+
95+
### "I need to debug a script that fails silently"
96+
97+
```bash
98+
mlcr <tags> --verbose # full debug output
99+
mlc find cache --tags=<failing-dep> # check if dep is cached
100+
mlc rm cache --tags=<failing-dep> # clear dep cache and re-run
101+
mlc show cache --tags=<script-tags> # inspect cached new_env
102+
cat ~/MLC/repos/local/cache/*/mlc-cached-state.json | python3 -m json.tool
103+
```
104+
105+
Common root causes:
106+
- Dep cached but stale → `mlc rm cache --tags=<dep-tags>`
107+
- `new_env_keys` missing from meta.yaml → key is silently dropped at the boundary
108+
- `preprocess` returned success but didn't set the expected env var → add assertion
109+
- `MLC_TMP_*` var expected downstream but not in `new_env_keys` → rename it or declare it
110+
111+
### "I need to understand why an env var is not reaching a script"
112+
113+
Env propagation rules:
114+
1. CLI `--key=val` → `input_mapping` → `env[MAPPED_VAR]`
115+
2. `env` passes to deps unless dep has `clean_env_keys` that matches
116+
3. Dep's `new_env_keys` is the **only** way values come back from a dep
117+
4. `MLC_TMP_*` never cached, never passed to deps automatically
118+
5. If a parent caches, only declared `new_env_keys` are replayed on cache hit
119+
120+
### "I need to run the inference benchmark locally for testing"
121+
122+
```bash
123+
# Quickest smoke test — resnet50, onnxruntime, CPU, 500 samples
124+
mlcr run-mlperf,inference,_submission,_short,_r6.0-dev \
125+
--model=resnet50 --implementation=mlcommons-python \
126+
--backend=onnxruntime --device=cpu \
127+
--scenario=Offline --test_query_count=500 --target_qps=1 \
128+
--hw_name=my_machine --quiet
129+
130+
# Find performance (tunes target QPS before a real run)
131+
mlcr run-mlperf,inference,_find-performance,_short,_r6.0-dev \
132+
--model=resnet50 --implementation=mlcommons-python \
133+
--backend=onnxruntime --device=cpu \
134+
--scenario=Offline --hw_name=my_machine --quiet
135+
136+
# Accuracy-only run
137+
mlcr run-mlperf,inference,_accuracy-only,_short,_r6.0-dev \
138+
--model=resnet50 --implementation=mlcommons-python \
139+
--backend=onnxruntime --device=cpu \
140+
--scenario=Offline --hw_name=my_machine --quiet
141+
142+
# Full submission run (both modes + compliance + checker + tar)
143+
mlcr run-mlperf,inference,_submission,_full,_r6.0-dev \
144+
--model=resnet50 --implementation=mlcommons-python \
145+
--backend=onnxruntime --device=cpu \
146+
--scenario=Offline --execution_mode=valid \
147+
--submitter=MLCommons --hw_name=my_machine --quiet
148+
```
149+
150+
### "I need to inspect or clear cache"
151+
152+
```bash
153+
mlc find cache --tags=get,mlperf,endpoints # find cache folder
154+
mlc show cache --tags=get,mlperf,endpoints # print new_env snapshot
155+
mlc rm cache --tags=get,mlperf,endpoints # delete specific cache
156+
mlc rm cache -f # delete ALL caches
157+
mlc prune cache # delete expired (past cache_expiration)
158+
```
159+
160+
### "I need to run in Docker"
161+
162+
```bash
163+
mlcd app,mlperf,inference,endpoints,_echo-server --num_samples=50
164+
# Rebuilds image if meta.yaml docker: section changed:
165+
mlcd app,mlperf,inference,endpoints --docker_rebuild
166+
```
167+
168+
---
169+
170+
## Quick reference: meta.yaml field cheat-sheet
171+
172+
| Field | What it does |
173+
|---|---|
174+
| `alias` | Script directory name and lookup key |
175+
| `uid` | 16-hex unique ID; never change after first commit |
176+
| `automation_uid` | UID of the `script` automation type (`5b4e0237da074764`); the only automation type currently in this repo |
177+
| `tags` | Comma-separated discovery tags; must be a superset of what callers request |
178+
| `category` | Grouping label in docs |
179+
| `default_env` | Env vars set before variation env (lowest priority) |
180+
| `new_env_keys` | **Only** these keys leave the script; use `*` and `?` wildcards |
181+
| `input_mapping` | CLI `--key` → `ENV_VAR` translation |
182+
| `deps` | Scripts to run before `preprocess()` |
183+
| `prehook_deps` | Scripts to run after `preprocess()`, before `run.sh` |
184+
| `posthook_deps` | Scripts to run after `run.sh`, before `postprocess()` |
185+
| `post_deps` | Scripts to run after `postprocess()` |
186+
| `variations` | Named parameter sets; `group:` makes them mutually exclusive |
187+
| `add_deps_recursive` / `adr` | Override tags/versions on named deps deep in subtree |
188+
| `versions` | Per-version dep overrides and env |
189+
| `cache` | Enable caching (default: false) |
190+
| `cache_expiration` | Auto-invalidate after `1h` / `1d` / `1w` |
191+
| `tests` | Inline test cases run by `mlct` |
192+
193+
---
194+
195+
## customize.py hook signatures
196+
197+
```python
198+
def preprocess(i): # before run.sh
199+
def postprocess(i): # after run.sh
200+
def predeps(i): # before dep execution
201+
def postdeps(i): # after dep execution
202+
```
203+
204+
All receive the same `i` dict:
205+
```python
206+
i['env'] # mutable env dict → set vars here
207+
i['automation'] # ScriptAutomation instance → i['automation'].logger
208+
i['os_info'] # dict from detect-os
209+
i['meta'] # parsed meta.yaml
210+
```
211+
212+
All must return `{'return': 0}` or `{'return': 1, 'error': 'reason'}`.
213+
214+
---
215+
216+
## Env variable naming conventions
217+
218+
No rigid rules — just two hard constraints and a naming guideline:
219+
220+
- **`MLC_` prefix** — use on all env vars set by MLC scripts so they are clearly distinguishable from the surrounding environment.
221+
- **`MLC_TMP_*`** — reserved for transient vars: never cached, never passed to deps. Use this when a value is only needed within the current script's run.
222+
- **`+VAR` in `new_env_keys`** — prepend to an existing env var (e.g. `+PATH`); the engine handles concatenation.
223+
224+
Everything else: name vars to reflect the script they come from and what they hold. Keep names reasonably short and meaningful — no other convention is enforced.
225+
226+
---
227+
228+
## Files to read first by task type
229+
230+
| Task | Read these first |
231+
|---|---|
232+
| Understand the inference benchmark | `script/run-mlperf-inference-app/meta.yaml`, `customize.py` |
233+
| Reference for a new MLPerf benchmark (dep chain, variations, dispatch pattern) | `script/app-mlperf-inference-mlcommons-python/meta.yaml`, `script/run-mlperf-inference-app/meta.yaml` |
234+
| Debug env propagation | `automation/script/module.py` (search: `new_env_keys`, `clean_env_keys`) |
235+
| Debug caching | `automation/script/cache_utils.py` |
236+
| Add a script | Nearest similar script + `automation/script/meta_schema.py` |
237+
| Fix meta.yaml format | `automation/script/lint.py` |
238+
| Run CI locally | `.github/workflows/test-mlc-script-features.yml` |
239+
| Understand Docker path | `automation/script/docker.py` |
240+
241+
---
242+
243+
## Don'ts
244+
245+
- Don't use `print()` in `customize.py` — use `i['automation'].logger`
246+
- Don't raise exceptions for recoverable errors — return `{'return': 1, 'error': '...'}`
247+
- Don't edit `mlc-cached-state.json` or `tmp-env.sh` by hand
248+
- Don't push directly to `main` — always open a PR; use `dev` only for urgent merges without approval
249+
- Don't hard-code paths in `run.sh` — use env vars set by `preprocess`
250+
- Don't change `automation_uid: 5b4e0237da074764` — it's the UID of the `script` automation type, not a script-specific value
251+
- Don't commit API keys — pass via `--api_key=...` only
252+
- Don't set `MLC_TMP_*` in `new_env_keys` (it's a transient namespace)

0 commit comments

Comments
 (0)