From c40f2cce8d8a637d0fc5b97d9014f9a7dc5b65cf Mon Sep 17 00:00:00 2001 From: alanhc Date: Thu, 3 Sep 2026 18:47:27 +0800 Subject: [PATCH] Add an AGENTS.md that routes rather than restates An agent arriving with no context reads whatever file its harness hands it, and this tree had none. CONTRIBUTING.md is found by name only, and the skills under .claude/skills/ are loaded by description rather than read in order. Both were already here; nothing said which to open first. AGENTS.md is the file every harness looks for, and CLAUDE.md is a symlink to it for the ones that look for that instead, the way .agents/skills already symlinks .claude/skills. It carries no rule of its own, since a rule copied to a second place is one that can drift. What it adds is the reading order, and the two prose rules a first sentence breaks before any skill has been loaded. check-skill-refs.py already took a routing document on the command line, so make check-skill-refs and the lint workflow name AGENTS.md. A stale pointer in it now fails the way a stale pointer in a skill does. --- .github/workflows/lint.yml | 2 +- AGENTS.md | 70 +++++++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + mk/tests.mk | 4 +-- scripts/check-skill-refs.py | 6 ++-- 5 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 AGENTS.md create mode 120000 CLAUDE.md diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 90ef219e..64d86646 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -177,7 +177,7 @@ jobs: # current. This found two dead references to a workflow and a makefile # that the CI split had already removed. if: ${{ !cancelled() }} - run: python3 scripts/check-skill-refs.py + run: python3 scripts/check-skill-refs.py AGENTS.md # What follows needs LINT_PKGS, and the three checks that use it read # only C sources, the style file, and the .ci scripts that implement diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..fc599e50 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,70 @@ +# Working on elfuse + +elfuse runs Linux ELF binaries from the macOS shell: each guest runs in a +Hypervisor.framework VM the `elfuse` process owns, and Linux syscalls are +translated to macOS behavior in host-side handlers rather than served by a +kernel. `README.md` states the scope and the limitations, `docs/internals.md` +the design, `docs/testing.md` the build and lane requirements. + +This file routes. It restates nothing that is settled elsewhere, because a +rule copied to a second place is a rule that can drift. + +## Before writing C + +`CONTRIBUTING.md` is the tracked style guide and it wins over everything +here. Read it rather than working from a memory of typical C style: several +of its rules are not the common default. + +## The skills + +The conventions `CONTRIBUTING.md` does not cover live in `.claude/skills/`, +reachable as `.agents/skills/` as well. Each directory holds one SKILL.md +whose description states when it applies; read the one covering the change +before making it, not after review asks for it. + +- `elfuse-conventions`: comments, commit messages, PR text, `docs/`, naming, + atomics, and the prose register every written surface obeys. +- `elfuse-syscall`: adding or changing a Linux syscall, the translation + boundary, `src/syscall/dispatch.tbl`, fd classes, lock order. +- `elfuse-guest-abi`: HVC calls, the EL1 shim, page tables, TLBI, and + anything that changes what the guest observes on return. +- `elfuse-verify`: which lanes an area needs, the test matrix, and the + Frama-C proof targets. +- `elfuse-security`: the guest as an attacker, and what a handler on the + trust boundary owes. +- `elfuse-debug`: a guest that faults, hangs, or answers the wrong errno. +- `elfuse-refactor`: behavior-preserving cleanup of code that works. +- `elfuse-skills`: editing these skill files themselves. + +## Gates + +`make check` builds, runs the tests, and runs the checkers. `make indent` +formats and `make check-format` verifies without rewriting; between them +they settle most review comments before they are written. + +Run what the change touched before calling the work done, and say what ran +and what it reported; effort is not a finding. `elfuse-verify` settles which +lanes an area needs, and which of them skip rather than fail when a tool is +missing. + +## Prose + +Two rules bind before the first sentence. Source comments and commit +messages are ASCII with no markdown syntax, which `make check-ascii` and the +commit-message hook enforce. The em dash (U+2014) is banned on every surface, +including the ones no gate reaches: `docs/`, PR bodies, and review replies. A +spaced double hyphen carries the same register rather than avoiding it. + +The rest of the machine register, effort claims and inflation words and +prompt echo among them, is defined once in +`.claude/skills/elfuse-conventions/references/prose-register.md`. A PR thread +is a technical discussion between people; walkthroughs, status tables, and +replies that hand the choice back to the reviewer are rejected on sight. + +## Files that are not yours + +Untracked working docs at the repo root or under `.claude/` belong to +whoever put them there. Never `git add`, commit, stage, gitignore, or delete +another person's: untracked and visible in `git status` is deliberate. +Anything that has to survive a fresh clone belongs in `CONTRIBUTING.md`, +`docs/`, or a skill. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/mk/tests.mk b/mk/tests.mk index 3555ba3d..adbfd194 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -97,10 +97,10 @@ check-svc-tails: @python3 scripts/check-svc-tails.py --self-test @python3 scripts/check-svc-tails.py -## Verify every path, target, and section the skills name still resolves +## Verify every path, target, and section the skills and AGENTS.md name resolve check-skill-refs: @python3 scripts/check-skill-refs.py --self-test - @python3 scripts/check-skill-refs.py + @python3 scripts/check-skill-refs.py AGENTS.md define RUN_OPTIONAL_SKIP77 @set -e; \ diff --git a/scripts/check-skill-refs.py b/scripts/check-skill-refs.py index 1203305d..8e850a36 100755 --- a/scripts/check-skill-refs.py +++ b/scripts/check-skill-refs.py @@ -555,9 +555,9 @@ def main(): SKILL_DIR.glob("*/references/*.md") ) - # A routing file is checked the same way, but only when its owner names - # it: whether one exists is local to a working copy. It is checked even in - # a clone with no skills directory, where its references rot fastest. + # A routing file is checked the same way, when named: AGENTS.md is the + # tracked one and a working copy may name its own. It is checked even in a + # clone with no skills directory, where its references rot fastest. for extra in sys.argv[1:]: path = pathlib.Path(extra) if not path.is_absolute():