| # AGENTS.md | |
| This file gives the operating rules for AI coding agents in this repository. Claude Code | |
| loads this file through `CLAUDE.md`. Other agents read it directly. | |
| This file is the short contract, and you must follow it. Other files tell you more: | |
| - [PLAN.md](PLAN.md) says what to build, and in which order. | |
| - [CONTRIBUTING.md](CONTRIBUTING.md) says how to set up and run things. | |
| - [docs/](docs/) holds the design that this code implements. | |
| An instruction in this file can conflict with an explicit request from the user. Then ask | |
| before you override the instruction. | |
| ## Non-negotiables | |
| - **Ask before you build an ambiguous feature.** A request can have more than one reading: | |
| which crate it touches, what the on-disk shape is, what "done" looks like. Then ask | |
| which reading is correct before you write code. A wrong guess costs an implementation | |
| and its tests. When the reading is settled, build all of the feature. Do not stop | |
| again to ask. | |
| - **The entry format is the contract.** `crates/core` defines the frontmatter, the bundle | |
| manifest, the order of the labels and the order of the trust tiers. A change in | |
| `crates/core` changes every other crate, the on-disk record, and the meaning of an | |
| export. Read [crates/core/AGENTS.md](crates/core/AGENTS.md) before you change | |
| `crates/core`. | |
| - **`make check` must pass before you commit.** It runs: | |
| - `cargo fmt --check`; | |
| - `cargo clippy`, with warnings as errors; | |
| - `cargo test`; | |
| - the tests of the guard; | |
| - other checks that the `Makefile` names. | |
| Fix every error and every warning. Do not commit with `--no-verify`. `make init` | |
| installs `make check` as the pre-commit hook. Some changes touch only Markdown files | |
| outside `crates/` and `taps/`. For this rule, an `AGENTS.md` in `crates/` or `taps/` | |
| counts as Markdown. The hook and CI do not run `make check` for such a change, because | |
| nothing builds from those files. `scripts/docs-only.sh` decides which changes are of | |
| this type. Its test holds the premise that no crate or tap embeds those files or reads | |
| them. Some scripts read them: | |
| - `scripts/seed-corpus.py` seeds the demo record from `docs/` and | |
| `examples/decisions/` (`make demo`). `golden/docs.toml` and `evals/demo.toml` score | |
| answers against that text. | |
| - Act 11 of the demo (`scripts/demo/11-limits.sh`) prints the two lists at the top of | |
| `README.md`. | |
| `make corpus-check` (`scripts/corpus-check.sh`) seeds a scratch record from those | |
| files. It fails when a golden question (`golden/docs.toml`, `golden/platform.toml`, | |
| `golden/acts.toml`) or an eval task names an entry that the record does not hold, when | |
| the keywords of a golden question do not find its answer, when the text of an eval | |
| task's entry no longer matches its `expect`, or when act 11 does not print every item | |
| of its two lists. CI runs it on every change, a docs-only change included. It does not | |
| run a harness, so it does not show what a model answers. | |
| The demo seeds its record only once. So, after you change a file in `docs/` or | |
| `examples/decisions/`, seed a new record with `make demo-ready RESET=1`. Then run | |
| `make golden` and act 6 (`make demo-all HARNESS=codex ACTS=6`). Act 6 needs a key | |
| for llm.apache.org, in `local/.virtual-key` or `LLMAO_KEY`. Without a key, the act | |
| says "skipped" and checks nothing. Neither command fails on a miss. Read what they | |
| print: golden's "Answers naming no entry in this record" and "Missed" lists, and the | |
| `passed` column of act 6's `memory` row. After you change those two lists, run act 11 | |
| (`make demo-all HARNESS=codex ACTS=11`). | |
| - **Show that a new assertion fails before you trust it.** Run it against the code as it | |
| was before your change. Put what it reported in the pull request. If a test only ever | |
| passed, nobody has shown that it tests anything. Assert the rule, not a bound on the | |
| rule. | |
| - **A flaky test is a defect. Fix it.** A flaky test passes and fails on the same code. | |
| It hides real failures. Find the cause and remove the cause. Possible causes are clock | |
| time, test order, load, shared state and outside services. Do not add an automatic retry | |
| or a skip, in the test or in the runner, to make the test pass. Make a timeout longer | |
| only if it was shorter than a delay that the test must tolerate. Give that reason in the | |
| commit message. If your change did not cause the flaky test, fix the test on a separate | |
| branch. Until that fix merges, you may run the failed check again on your branch: | |
| `make check`, `make e2e`, or the CI job. Name the flaky test in your PR. Each fix | |
| must let CI find the next failure of that kind without a person. If a test fails only | |
| under load, make the test create the load, or add a CI job that does. If a test fails | |
| only on one platform, make sure that CI runs it on that platform. The browser tests | |
| (`make e2e`) run once more after a failure on CI, to record a trace. A test that then | |
| passes fails the run (`e2e/playwright.config.ts`). | |
| - **Stage only the files that you changed** (`git add <path>`). Never use `git add -A`, | |
| `git add .` or `git commit -a`. | |
| - **Work in your own worktree, and expect other work in the repository.** Several sessions | |
| and several people work in this repository at the same time, one for each topic: | |
| ```sh | |
| git worktree add ../durable-memory-<topic> -b feat/<topic> origin/main | |
| ``` | |
| Never switch the branch of another worktree. Never use a bare `git stash`, because all | |
| the worktrees share one stash stack. To set work aside, make a temporary commit. | |
| - **Nothing engine-specific goes outside the backend that needs it.** The record is | |
| versioned Markdown. The paper names no storage engine. This code also names no storage | |
| engine outside the backends in `crates/store`, `crates/index` and `crates/ledger`. If a | |
| doc or a core type says "SQLite" or "S3", that is a bug. | |
| - **The command line and the memory server call the same library functions.** Nothing is | |
| only in the binary. | |
| - **Before you change the web pages, read the design book and load the `frontend-design` | |
| skill.** Do both, every time. [docs/design-book.html](docs/design-book.html) says what | |
| each colour means and how to compose a page. `crates/web/static/tokens.css` holds the | |
| same values. If you add one of these, change the book in the same commit as the code: | |
| - a token; | |
| - a hue; | |
| - a chip; | |
| - a glyph; | |
| - a type role. | |
| In the Decisions chapter of the book, say why. | |
| - **Only two crates may use `unsafe`.** `crates/sandbox` may use it, because the | |
| namespaces and the mounts must occur between fork and exec. `crates/wslc-api` may use | |
| it, because it binds a C API by hand. Every `unsafe` block has a SAFETY comment. In all | |
| other crates, the workspace forbids `unsafe`. Do not relax that rule. If a crate needs | |
| foreign calls, give it its own binding crate. Do not make a third exception. | |
| `make check` holds both halves of this rule: `scripts/unsafe-crates.sh`, and clippy for | |
| the Windows target of the binding. | |
| - **No image leaves this machine.** Nothing in this repository pushes an image to a | |
| registry, logs in to a registry or makes a GitHub release. There are two reasons. The | |
| harness image holds harnesses that the project may not redistribute. The image is also | |
| built from unreleased source. `make check` runs `scripts/no-image-push.sh`, which | |
| applies this rule to the files of the repository. The guard applies it to the commands | |
| that a session types. | |
| images/harness/README.md gives the reasons. | |
| - **Nothing has access outside its root.** The wrapper, the per-session memory server and | |
| its egress, is a confined process. It has access only to the record, its own state and | |
| its grants file, each through a rooted operator. It has access to nothing else on the | |
| host. Code validates a name before the name becomes a path. Code is a bug if it does one | |
| of these things: | |
| - It opens a file by an absolute path. | |
| - It reads an environment variable for a location. | |
| - It follows a path that it was given. | |
| The exception is the binary when it resolves its own mounts at startup. | |
| ## Some of these are held for you | |
| The repository enforces the rules above that read as absolutes. It does not rely on | |
| memory for them. Thus, to break one of these rules takes a decision, not a lapse. The part | |
| of the Claude Code harness that belongs to the repository is committed under `.claude/`: | |
| | Path | What it is | | |
| | ---- | ---------- | | |
| | `.claude/settings.json` | the permission allowlist (cargo, make, and read-only `git` and `gh`) and the hook registration | | |
| | `.claude/hooks/guard-bash.sh` | It refuses a command that does one of these: stages with a wildcard; runs `git stash` bare or with `save`, `pop`, `clear` or `drop`, or a `git stash push` without a message; passes `--no-verify`; pipes `cargo` or `make` into `head` or `tail`; pushes an image, logs in to a registry or makes a GitHub release; posts an issue or pull request body that does not say who wrote it | | |
| | `.claude/hooks/guard-bash.test.sh` | what the guard refuses, and what it must continue to allow. `make check` runs it | | |
| | `.claude/commands/adversarial.md` | the prompt for the pre-PR pass, and the classes that the pass attacks | | |
| | `.claude/commands/reader-test.md` | the prompts for a docs-only branch, instead of that pass: a reader that has only the changed files and their diff, and a fact check against the code | | |
| If the guard refuses something that this repository asks for, the guard has a bug. Add | |
| the case to the test and fix the guard. Do not work around the bug. `.gitignore` ignores | |
| `.claude/settings.local.json` and `.claude/worktrees/`. Do not commit other files in | |
| `.claude/` unless they belong to the repository. An agent that is not Claude Code gets | |
| none of this. The same rules apply to that agent, and it learns them when it reads them. | |
| ## Project shape | |
| | Crate | Holds | Compiles to | | |
| | ----- | ----- | ----------- | | |
| | `crates/core` | entries, frontmatter, bundles, digests, labels. No I/O | native, wasm, WASI | | |
| | `crates/store` | the record over OpenDAL: versions, tombstones | native | | |
| | `crates/index` | search index, summaries first; backends behind one trait | native | | |
| | `crates/ledger` | tasks, leases, origin dedup; backends behind one trait | native | | |
| | `crates/policy` | the grants file, labels, ceilings | native, wasm | | |
| | `crates/server` | the service that the command line and the MCP handler call: T0, bundles, and the memory server over MCP | native | | |
| | `crates/egress` | the egress, the one way out: it forwards with ceilings and usage, and carries MCP over HTTP | native | | |
| | `crates/sandbox` | the sandbox: the plan, a namespaces sandbox backend for Linux, and Seatbelt for macOS (dry-coded) | native | | |
| | `crates/memory` | the binary, `amem`: serve, launch, relay, doctor, and the command line | native | | |
| The code uses Rust stable, in one workspace, with `cargo` on the host. Put a rule that | |
| applies everywhere in this file. Put a rule that applies to one crate in an `AGENTS.md` in | |
| that crate. There, whoever is about to break the rule reads it. | |
| ## Quality gates, and where each lives | |
| This repository follows the conventions of Rust as they are. The thresholds are in files, | |
| not in memory. | |
| | Concern | Convention here | Where | | |
| | ------- | --------------- | ----- | | |
| | Tests | Unit tests in a `#[cfg(test)] mod tests` beside the code; integration tests in each crate's `tests/`; doc tests on public examples | `cargo test --workspace` | | |
| | Formatting | rustfmt, Unix line endings on every platform | `rustfmt.toml` | | |
| | Lints and complexity | clippy with warnings as errors; `cognitive_complexity` and `too_many_lines` enabled, thresholds in `clippy.toml`; `unwrap_used` outside tests; `missing_docs` on public items | `Cargo.toml` `[workspace.lints]`, `clippy.toml` | | |
| | Coverage | `cargo llvm-cov`, line coverage over the workspace, `COVERAGE_MIN` in the Makefile | `make coverage` | | |
| | Dependencies | `cargo deny` for advisories, licences, duplicates, sources; `cargo machete` for unused | `deny.toml`, `make deny`, `make machete` | | |
| | Portability | the tests of the workspace run on Linux and Windows; `dm-core` builds for `wasm32-unknown-unknown` | CI, `make wasm-check` | | |
| Two of these values can move in one direction only. **You may raise `COVERAGE_MIN`, and | |
| you may not lower it.** Raise it in the commit whose tests get the coverage to the new | |
| value. **You may not raise the thresholds in `clippy.toml`** to make a reading pass. Split | |
| the function. An `#[allow]` on a lint is a decision. Say why in a comment on the same | |
| line. | |
| `make check` is the pre-commit gate, and it is fast. CI runs `make verify`. It adds | |
| coverage, dependencies and the wasm build. Run `verify` before you push. | |
| Builds are faster when you install `sccache` and `cargo-nextest` | |
| (`cargo binstall sccache cargo-nextest`). The Makefile uses them when they are present, | |
| and it needs nothing from you. The dev profile keeps only line tables. Thus you can still | |
| read backtraces, and the target directory stays small. If the target directory grows, | |
| `cargo sweep --time 7` removes stale artifacts. The plan for CI is remote execution | |
| (PLAN.md, Build speed). Until then, the cache is local. | |
| ## Pull requests | |
| - Branch from `main`. Make sure that `make check` passes. Then open the PR against `main`. | |
| - **A fresh subagent attacks the branch after the push and before the pull request.** It | |
| attacks again after each push that revises an open pull request, before you request | |
| review again. The prompt is `/adversarial`, in `.claude/commands/adversarial.md`. Use a | |
| fresh agent, never a fork. A fork inherits the reasoning that wrote the branch, and it | |
| agrees with that reasoning. Answer what the subagent finds as you answer a reviewer. In | |
| the PR description, say that the pass ran and what it found. Thus the PR also shows a | |
| pass that found nothing. | |
| - **A docs-only branch gets a reader test instead**, at the same points. A docs-only | |
| branch is a branch whose changed paths `scripts/docs-only.sh` accepts. The prompt is | |
| `/reader-test`, in `.claude/commands/reader-test.md`. First, write the questions that | |
| the change should answer. Then one fresh agent answers them. It uses only the changed | |
| files, their diff, and what every session loads. Another agent checks each claim against | |
| the code. A wrong or missing answer is a finding against the text. The command says when | |
| a revision needs both agents again, only the fact check, or neither. In the PR | |
| description, give: | |
| - that the pass ran; | |
| - the questions; | |
| - how the reader did; | |
| - what the fact check found; | |
| - what is still open. | |
| - **A review applies to one commit, so request it again after every push.** Do not read a | |
| clean round as a verdict. | |
| ## Writing in ASD-STE100 | |
| Write in [ASD-STE100](https://www.asd-ste100.org/) (Simplified Technical English): | |
| - Keep each sentence short. Use not more than 20 words in an instruction and not more | |
| than 25 words in a description. | |
| - Write about one topic in each sentence. | |
| - Use the active voice and the simple tenses. | |
| - Use one word for one thing. Do not use a different word for variety. The words are in | |
| [docs/terms.md](docs/terms.md); add a term there in the commit that first needs it. | |
| - Do not use idioms or figurative language. | |
| This applies to: | |
| - pull request descriptions, comments on issues and pull requests, and commit messages; | |
| - `README.md`, `CONTRIBUTING.md`, `local/README.md`, the READMEs beside the code, and the | |
| Markdown documents in `docs/`; | |
| - the AGENTS files, and the commands in `.claude/commands/`. | |
| These keep their own style: the paper (`docs/durable-memory-for-disposable-agents.md`), | |
| the paper's appendix (`docs/durable-memory-for-disposable-agents-appendix.md`), the case | |
| study (`docs/seeding-a-foundation.md`), `PLAN.md`, the records in `spikes/`, the example | |
| records in `examples/`, code comments, and the text of the web pages and the design book. | |
| ## Writing on an issue or a pull request | |
| A session posts a comment under the account of a person, and colleagues read it. | |
| - **Answer only what the request asks, and then stop.** | |
| - **Write in ASD-STE100**, as the section above says. | |
| - **The account holder approves the text first.** You cannot unsend a comment. | |
| - **Say who wrote it**, on a line of its own at the end. The guard refuses a `gh` command | |
| that posts a body without such a line: | |
| ```text | |
| Posted by Claude on behalf of @sstriker | |
| ``` | |
| ## Reading your own results | |
| - **A pipe does not keep the exit status.** `cargo test | tail -20` reports the exit | |
| status of `tail`. To get the output and the exit status, send the output to a file: | |
| `cargo test > /tmp/out.log 2>&1; echo "exit=$?"`. | |
| - **When the guard refuses a command, no part of the command runs.** The guard stops the | |
| whole call. | |
| - **Do not state a number, or a claim about what the code does, that you did not just | |
| produce.** A measurement comes from a command that you ran in this session. A statement | |
| about what code does comes from code that you just read or ran. When it costs little, | |
| name the evidence: the command, or the `file:line`. | |
| ## Conventions | |
| - Start each commit subject with a conventional-commit prefix: `feat:`, `fix:`, `chore:`, | |
| `docs:`, `test:`, `refactor:`, or `ci:`. When an agent writes a commit, end the commit | |
| with a `Co-Authored-By:` trailer that names the agent. | |
| - Do not use emojis in commits, pull requests, issues or code. Keep the text short and | |
| direct. | |
| - **A comment says what the code does, not how the code got that way.** Put history in a | |
| comment only when it names a shape that the code avoids, and an edit could bring that | |
| shape back. | |
| - Do not remove or downgrade code to stop a warning from an outdated dependency. Upgrade | |
| the dependency. | |
| - Ask before you remove functionality or code that seems intentional. |