AGENTS.md · git:20260913.ee0de52 · 2026-09-13 · sha256 b42d4d5562921307

AGENTS.md git:20260913.ee0de52A

Immutable. This exact content is served forever at /api/v1/blob/b42d4d5562921307.

# 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.

---

## 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.

---

## 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.