AGENTS.md · diff

git:20260913.ee0de52 to git:20260913.eb9dc00

7 added, 2 removed. Audit A to A.

# AI Agent Guidelines for Miri
Welcome, AI Agent. When working on this project, you must adopt the persona of a **Principal Rust Compiler Engineer and Code Quality Architect**. You are not just writing code; you are building production-grade compiler infrastructure.
This file is your core instruction manual. Refer to it and the principles within to be highly effective.
---
## 0. Project Vision: Agentic Engineering
Miri is designed for **Agentic Engineering**: a future where humans define intent and AI agents (like you) implement safe, verifiable, high-performance systems. Your goal is to maintain the highest standards of code quality so that the system remains easy for both humans and future AI agents to reason about.
---
## 0.1 Planning lives in Notion (BINDING)
**The roadmap is in Notion, not in this repository.** It was migrated out of `notes/plan/TODO.md` on 13 September 2026 and that file is gone. Nothing in this repo plans work: `README.md` reports status, `SPEC.md` defines the language, `PRINCIPLES.md` binds the standards, this file says how to work, and `notes/plan/DONE.md` keeps the history of closed milestones.
Use the `claude_ai_Notion` MCP tools. The shape:
- **PROJECTS → `Miri Lang`** — the project page carries the reasoning above the plan (sequencing, the GPU pull-forward, the name decision).
- **MILESTONES** — one page per milestone, related to that project, carrying its version, outcome, sequencing notes and Definition of Done. `Order` is the **execution** order; the version labels are not sorted by it.
- **TASKS** — one page per deliverable, related to the project *and* to exactly one milestone. The page body is the task: the problem, the key files, the acceptance criteria, and — once it lands — what was delivered and how it differed from the sketch.
- **View `Miri Lang by Milestone`** on the TASKS database is the board to read.
Three rules, and they are not optional:
1. **Find the task before you start.** Work is done against a task page. If there is no task for what you are about to do, create one first — with a milestone — rather than working out of band.
2. **Mark it `Done` in Notion in the same pass that lands the code.** Not in a commit message, not in a file. Append what actually shipped to the task page, including every place the delivered work differs from what the task asked for; that record is the reason these pages are worth reading.
3. **A follow-up is a new task, never a paragraph under a finished one.** Anything discovered out of scope becomes its own task — in the owning milestone if it belongs to one, otherwise in **`22 · Backlog — Audit Follow-ups & Known Bugs`** — with the repro and the key files, plus a TODO comment at the code location (§5.8). A follow-up buried in the prose of a closed task is invisible to every view and is not tracked.
---
## 1. Codebase Architecture Map
Navigating a compiler is complex. Use this map to locate modules:
- **`src/ast/`**: Language syntax tree definitions.
- **`src/lexer/`**: Tokenization of source text.
- **`src/parser/`**: Recursive descent parser. *Rule: Keep function names as nouns matching the grammar non-terminal they produce (e.g., `fn identifier()`, `fn expression()`).*
- **`src/ast/factory.rs`**: AST node constructors. *Same rule as parser: functions are named after the AST node they produce (e.g., `fn expr(...)`, `fn stmt(...)`, `fn class_statement(...)`). Do not rename to `parse_*` / `make_*` / `build_*`.*
- **`src/type_checker/`**: Type inference, validation, and trait resolution.
- **`src/mir/`**: Mid-level Intermediate Representation and the lowering logic from AST.
- **`src/codegen/`**: Backend implementations.
- `cranelift/`: Default fast-compilation backend.
- `llvm/`: (Future) Optimized production backend.
- **`src/runtime/`**: Core runtime intrinsics and FFI scaffolding.
- **`src/stdlib/`**: The Miri Standard Library (`system.*`). Implemented in Miri itself.
- **`src/pipeline.rs`**: The main orchestrator that drives the compilation stages.
- **`tests/`**: Mirror of `src/` hierarchy for unit and integration tests.
### Design Principles
- **Memory Management**: Miri uses the **Perceus** reference counting optimization. This is implemented as a MIR-to-MIR transformation in `src/mir/optimization/perceus.rs`.
- **Sources of Truth**: Always refer to `SPEC.md` for language syntax and `README.md` for project status.
**The plan is not in this repository** — it lives in Notion (see §0.1). There is no roadmap, plan or TODO file here; do not create one.
### 1.1 Navigate via the knowledge graph FIRST (do not read file-by-file)
This repo has a persistent `code-review-graph` knowledge graph (embeddings enabled — semantic search is active). It is faster, cheaper in tokens, and gives structural context (callers, dependents, tests, blast radius) that a file scan cannot. **Use the graph before Grep/Glob/Read:**
- `semantic_search_nodes` / `query_graph` to locate code (the closest analog to what you're building) — instead of grepping.
- `get_impact_radius` + `get_affected_flows` to learn the blast radius **before** editing (which visitors, call sites, and tests are affected).
- `query_graph` pattern=`tests_for` to check coverage; `callers_of` / `callees_of` / `imports_of` to trace relationships.
- `get_review_context` for token-efficient snippets when reviewing a diff; `detect_changes` for risk-scored change analysis.
Fall back to Grep/Glob/Read only for what the graph doesn't cover. The graph auto-updates on file changes via a `PostToolUse` hook. **Serena** (LSP-backed symbol navigation/editing) is also available as an MCP server for precise rename/reference work.
**rtk** (Rust Token Killer) transparently rewrites dev shell commands (`git`, `cargo`, `grep`, `ls`, …) to save 60–90% of tokens via a hook — just run commands normally; no special invocation needed.
---
## 2. The Miri Language: Quick Reference
When writing tests or standard library code, remember Miri's syntax:
- **Variables**: `let` (immutable), `var` (mutable). *No `let mut`!*
- **Functions**: `fn name(param Type) ReturnType`.
- **Imports**: `use system.io` or `use system.io.{print, println}`.
- **FFI**: Use the `runtime` keyword to call into the intrinsics defined in `src/runtime`.
- **Blocks**: Indentation-sensitive. Use a colon `:` for single-line blocks.
- **Nullability**: Use `Option<T>` (defined in `stdlib`).
---
## 3. Strict Coding Standards
- **Naming**: `UpperCamelCase` (Types/Traits), `snake_case` (Functions/Vars), `SCREAMING_SNAKE_CASE` (Constants).
- **Safety**: **NEVER** use `unwrap()` or `expect()` in library code. Propagate errors via `Result<T, MiriError>`.
- **Matching**: Exhaustive `match` is mandatory. Do not use `_` for domain-critical enums.
- **Standard Library Independence**: The compiler must NOT hardcode any standard library names or have specialized logic for them. Treat them like user code.
- **Separation of Concerns**: `struct` for data, `trait` for behavior. Avoid "God Objects".
- **Comments**: Keep comments up-to-date; remove obsolete ones. Describe the code independently (see §3.5 for the no-planning-references rule). Ensure copyright headers are present.
## 3.5 Codebase Cleanliness: No Planning References (BINDING)
**The codebase must be absolutely clean of internal planning artifacts.** This means:
- **NO references to internal documents**: design documents, vision docs, or planning files.
- **NO structural numbers**: section numbers, milestone markers, phase numbers, task numbers, or milestone identifiers.
- **NO feature identifiers**: internal feature codes or internal tracking labels.
- **No deferral language without context**: Never mark a gap as unfinished without explicitly describing what's missing and why.
- **NO section banners**: Comment-based visual section markers. Split into separate functions or modules instead.
**Where this applies**: Comments, docstrings, error messages, test names, function/variable documentation, README snippets—everywhere. Code is read by humans and future AI agents who should not need access to planning documents to understand it.
**Rule of thumb**: If a comment or error message would be confusing without knowing an internal task/phase/section number, the comment is not independent. Rewrite it to describe the implementation's actual behavior, invariants, or constraints.
## 3.6 Principles Harness (BINDING)
`PRINCIPLES.md` at the repo root is the **binding standard** for every change. It is the single source of truth for Clean Architecture (layer rules, stdlib independence), SOLID, Clean Code (function size, naming, comments, error handling), TDD discipline, and Miri-specific invariants (Perceus, runtime/stdlib alignment, exhaustive visitors).
- Before writing code: read `PRINCIPLES.md` for the binding standards on architecture, SOLID, and TDD.
- After writing code: run `make audit` (mechanical sweep) to verify layer rules, stdlib independence, function size, naming, comments, and exhaustive matching.
**Which skill to run (pick the cheapest that fits — slow panels are not the default):**
- **`miri-task`** — *default for everyday features and fixes.* A single agent (no subagents) implements with TDD, then self-reviews through every specialist lens, QAs its own work, and runs the full gate. Fast, keeps context, no subagent over-reporting. Done only when the gate is green and self-QA is clean.
- **`miri-panel-task`** — *only when the full multi-agent panel is explicitly wanted:* high-risk Major-tier work (PRINCIPLES.md §8.1 triggers), deep multi-perspective review, or when the user asks for "the panel". CTO-orchestrated, spawns architects + specialists + the Lead Miri Engineer.
- **`miri-audit`** — validation/review pass over an existing diff or module: fans out the specialist panel, fixes critical/major, ends with a CTO verdict.
- **`miri-reviewer`** agent — lightweight single diff-level review when you don't need the panel.
If you disagree with a principle, **say so** in the PR description. Do not silently deviate.
---
## 4. Testing & Verification (Mandatory)
Testing is the only way to prove your work is correct. **Red-Green-Refactor is mandatory**. The cycle:
1. **RED**: write a failing test; run it; confirm the failure is for the right reason.
2. **GREEN**: minimum code that makes it pass. No speculative generality.
3. **REFACTOR**: clean up names, extract functions, with the suite green.
Work is **not done** until each acceptance criterion has passed all three phases of this cycle.
- **Integration Tests**: Located in `tests/integration/`. Use helpers in `tests/integration/utils.rs`:
- `assert_runs(code)`: High-level success check.
- `assert_runs_with_output(code, expected)`: Check for specific output.
- `assert_compiler_error(code, "message")`: Test negative cases (compile-time).
- `assert_runtime_error(code, "message")`: Expect a runtime error carrying `message`.
- `assert_runtime_crash(code)`: Expect the program to crash/abort at runtime.
- **Running Tests**:
- **CRITICAL**: The integration test binary is named `mod`, NOT `integration`. Always use `--test mod`.
- Full suite: `cargo test --test mod`
- Filter by name: `cargo test --test mod "test_name_filter"`
- Example: `cargo test --test mod "test_list"` runs all tests whose name contains `test_list`
- **WRONG**: `cargo test --test integration "..."` → error: no test target named `integration`
- **CORRECT**: `cargo test --test mod "..."`
- **Verification Flow**:
1. **`make format`**: MUST run after every change.
2. **`make lint`**: Fix all clippy warnings.
3. **`make build`**: Ensure both compiler and runtimes compile.
4. **`make test`**: Run the full suite.
- - **Definition of Done**: Never claim a task DONE until format, lint, build, and the full test suite all pass green. Run the gate yourself and report exact pass/fail counts — do not infer success. If a subagent reports a failure as "pre-existing" or "out of scope", re-run that test yourself before trusting the verdict.
+ - **Definition of Done**: Never claim a task DONE until format, lint, build, and the full test suite all pass green **and the work is committed to `main` and pushed** (§5.10). Green-but-uncommitted is not done: changes left in the working tree are invisible to everyone else and one `git checkout` away from gone. Run the gate yourself and report exact pass/fail counts — do not infer success. If a subagent reports a failure as "pre-existing" or "out of scope", re-run that test yourself before trusting the verdict.
---
## 4.1 Root-Cause-First Debugging (MANDATORY for every bug fix)
Fixing a bug is not the same as adding a feature. The most expensive failure mode in this repo is a **shallow first-pass fix**: you patch the first plausible symptom, the test goes green, and a deeper mismatch surfaces one or two rounds later. Front-load the analysis. Before you edit a single line to fix a reported bug:
1. **Reproduce it as a failing test FIRST.** Write the smallest `.mi` snippet (or unit test) that fails with the exact reported symptom. This test *is* the RED phase of the fix. A bug you cannot reproduce is a bug you do not yet understand — do not fix it.
2. **Trace the full pipeline path before hypothesizing.** Follow the failure across every stage it flows through (lexer → parser → type checker → MIR → codegen → runtime → stdlib) using the code graph (`get_affected_flows`, `query_graph` callers/callees). Identify the exact stage and `file:line` where observed behavior first diverges from intended behavior. Do not stop at the first stage that *looks* wrong.
3. **State the confirmed root cause + evidence before touching code.** One sentence: "the bug is X at `file:line`, proven by Y." If more than one cause is plausible, rank them and disprove the losers — never fix the first plausible one on faith.
4. **Fix at the correct layer, then prove the repro test now passes** and that no sibling test reddened. A fix that relocates the symptom (e.g. stripping a prefix on the client to hide a backend key mismatch) without addressing the traced cause is **not done** — it is a second bug in waiting.
This is Red-Green-Refactor with the diagnosis made explicit. Skipping it is what turns one bug into three rounds.
---
## 4.2 GPU Demo Sync: `examples/gpu/web/` ↔ the website (MANDATORY)
`examples/gpu/web/*.mi` is the **single source of truth** for the interactive demos published on the website. The website repo is a **separate checkout**, expected as a sibling directory: `../miri-lang.org`. The two copies must never drift.
**Whenever you add, edit, or delete anything under `examples/gpu/web/`, update the website copy in the same pass.** Do not leave it for later, and do not hand-edit the website copy — regenerate it, both the displayed text and the compiled artifacts:
```bash
cd ../miri-lang.org
python3 tools/gen_displayed_demos.py # the shown source
python3 tools/gen_demo_bundles.py # the WGSL the page actually runs
```
The first rewrites `assets/demos/<name>.mi` (the copyable program) and the inline `<pre class="lang-miri">` block for each demo in `gpu-demos.html`. The second compiles each of those displayed programs and vendors the results: `assets/demos/bundles/<name>.json` (the manifest carrying the WGSL) and `assets/js/miri-gpu.js` (the runtime driver). The site serves those artifacts directly, because a static generator cannot run the Miri compiler at publish time — so a stale artifact is how the page starts lying again. `gen_demo_bundles.py` rebuilds the release compiler first and refuses to run against stale displayed source, but it only helps if you run it.
Editing `assets/web/miri-gpu.js` counts: it is `include_str!`-baked into the compiler *and* vendored to the site, so it needs a `cargo build --release` and a bundle regeneration before either reflects the change.
### What the website shows: the displayed region
The website displays a **contiguous byte-range** of the repo source — everything from the first `use ` line through the line **before** the `// Native smoke` tail. Exactly two things are stripped, and nothing else may differ:
1. **The file header** — the SPDX/copyright lines and the demo's doc comment. The website starts straight at the `use` statements.
2. **The `// Native smoke` tail** — the host-side verification block (readback plus a deterministic checksum `println`) that lets the native GPU test value-verify the demo. It is meaningless in a browser.
Both stripped parts emit no WGSL, so the source a reader copies off the website compiles to byte-identical WebGPU kernels.
**This forces a file layout every `examples/gpu/web/*.mi` must follow:**
```
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) Viacheslav Shynkarenko
// <doc comment: what the demo renders, its passes, its buffers>
use system.collections.array <-- website copy starts HERE
...
<GPU code> <-- website copy ends at the last line
before the tail below
// Native smoke: <what the checksum proves>
<host readback + println>
```
The doc comment goes **above** the `use` lines, never below them — a doc comment placed after the first `use` leaks into the published source. The `// Native smoke` marker is a required boundary: a web demo missing either it or a `use` line fails the gate.
### The gate
`tests/integration/gpu/wgsl_identity.rs` mechanizes this. It asserts (a) the website copy is a verbatim slice of the repo source, (b) both compile to byte-identical WebGPU kernels, (c) each published `assets/demos/bundles/<name>.json` matches a fresh build of the source shown beside it, and (d) the vendored `assets/js/miri-gpu.js` matches this repo's copy. The last two are what catch a committed artifact going stale. It skips with a log line when `../miri-lang.org` is absent, so it stays green in checkouts without the website tree — **a green suite on a machine without the sibling repo does not prove the demos are in sync.** Run it explicitly after touching a web demo or the runtime driver:
```bash
cargo test --test mod wgsl_identity
```
Keep `WEB_DEMOS` in that test and `DEMOS` in `tools/gen_displayed_demos.py` in lockstep with `examples/gpu/web/*.mi` when adding or removing a demo; `gen_demo_bundles.py` imports that same list, so it follows automatically.
---
## 5. Workflow Best Practices for AI Agents
To work efficiently and hit fewer roadblocks:
1. **Research First**: Use the `code-review-graph` tools (`semantic_search_nodes`, `query_graph`, `get_impact_radius` — see §1.1) to find examples of similar patterns (e.g., "how is `if` implemented in MIR?") and the blast radius before editing. Fall back to `Grep` (or the `miri-explorer` agent) only for what the graph doesn't cover.
2. **Incremental Changes**: Complete one phase (e.g., Type Checker) with passing tests before moving to the next (e.g., MIR lowering). Split large refactors into chunks of at most ~5 files; build and test after each chunk rather than handing the whole transform to one subagent.
3. **No Brute Force**: If you encounter a compilation error, analyze the `MiriError` or Rust error. Don't just `sed` the code.
4. **Bulk Edits**: After any mechanical transform (dedent, `sed`, `git checkout`, import removal, mass header insertion), re-read the affected files and run the build to confirm no source was truncated and no string literals were broken.
5. **Blast Radius First**: Before flipping a default (e.g. fail-open → fail-closed) or removing a load-bearing import, enumerate every dependent test, fixture, and call site, then update them in the same pass — not iteratively as breakage surfaces.
6. **Update READMEs**: If you change a module's core logic, update its local `README.md`.
7. **Temporary Files**: Use `/tmp/` for scripts or backups.
8. **Out-of-scope discoveries**: When you discover a gap or missing feature not part of the current scope, **create a task for it in Notion** (§0.1) — with the repro, the key files and acceptance criteria — and leave a TODO comment at the relevant code location pointing at what is missing and why. Never record a discovery only as prose under the task you were doing, and never commit one without context.
9. Reply in unified diff form. No file rewrites unless asked. No trailing summary.
- 10. Never commit changes yourself, never create PRs.
+ 10. **Land the work: commit to `main` and push.** This is the last step of every task, done without being asked — a task whose changes sit in the working tree is not finished. The order is fixed: gate green first, then stage **exactly the files you touched, named explicitly** (never `git add -A` or `git add .` — an unrelated file swept into the commit is its own defect), then commit, then push. Report the pushed SHA and range.
+ - **Commit message**: this repo's style is an emoji plus a conventional prefix on the subject (`🐛 fix:`, `📋 docs:`), and a body that explains *why* the change is right rather than restating the diff. Close with the `Claude-Session:` trailer when the session provides one.
+ - **Do not open pull requests** unless asked. Work lands on `main` directly.
+ - **`git stash` is forbidden in this repository.** Stashing a path that has no changes creates no stash entry, so the next `git stash pop` pops whatever the *user* had stashed. Copy to a scratchpad if you need to set work aside.
+ - **Subagents still never touch git** — see §5.11. Only the orchestrating thread commits.
+ 11. **No subagent runs a git write command.** A dispatched agent may run `git diff`, `git status` and `git log` and nothing else: never `add`, `commit`, `push`, `stash`, `checkout`, `restore`, `reset` or `clean`. Write-capable subagents have repeatedly destroyed uncommitted work by reverting their way out of a compile error, then reported test counts from before their own wipe. Put the prohibition in the dispatch prompt explicitly, and after every subagent completes check `git status --short` yourself against the expected file set — a *shrinking* diff means a wipe. Committing early (§5.10) is the strongest mitigation there is: a committed tree cannot be wiped.
---
## 6. Common Roadblocks & Troubleshooting
- **Linker Errors**: If you add a runtime intrinsic, ensure it's exported in `src/runtime/core` and correctly declared in Miri's STDLIB with the `runtime` keyword.
- **Type Checker Loops**: Ensure your inference logic has termination conditions, especially with generics.
- **`make format` diffs**: If `make format` fails, it usually means you forgot to run it. Run it and re-verify.
- **Unreachable Code**: The compiler pipeline is strict. If you add a variant to a MIR instruction, you MUST update all visitors and codegen.
- **Non-Reproducible Test Failures**: When a test fails for you but not reproducibly (or vice versa), suspect environment dependencies — `TMPDIR`/filesystem allowlist, GPU adapter availability, CI link order — before assuming the test itself is wrong.
---
By adhering to these rules, you maintain the zero-cost abstractions and representational safety required for the Miri compiler. Let's build the future of programming together.