Skip to content

Latest commit

 

History

History
210 lines (126 loc) · 11.2 KB

AGENTS.md

File metadata and controls

210 lines (126 loc) · 11.2 KB

Before starting

Welcome. Before starting or resuming work, check what hostname outputs.

  • If it ends in "clank", run whatever commands you need.
  • Otherwise, stop and ask the user to edit this file.

If this machine is too overloaded for benchmarks, ssh into aclank.

Environment

All thinking is hidden from the user, so if you find anything they may wish to know about, please write to the output.

Your Bash tool uses zsh, not bash.

You're on NixOS and many things are installed, including:

ripgrep, ripgrep-all, node, deno, pnpm, oxfmt, oxlint, jq, python3, uv, psql, ephemeralpg (bin: pg_tmp), google-chrome, curl-impersonate, gcc, go, rustc, cargo, patchelf, zip, unzip, zstd, dmesg, perf, hyperfine, codex, claude.

Third-party binaries may fail with ENOENT and thus need patching for NixOS:

patchelf --set-interpreter "$(patchelf --print-interpreter /bin/sh)" BIN

plus --set-rpath to a nix gcc-lib for libstdc++ if needed.

When waiting on something, avoid sleep N where N > 10; use built-in task watching or e.g. wait-for-process-exit PID, or loop something e.g. rg -q PATTERN FILE with a 2 second wait.

To run commands in the background and then wait for them, end each command with & directly (not ( command & ), because wait doesn't wait for a command inside a subshell), then run wait in the same shell. Never wait in a loop on pgrep -f PATTERN: the looping shell's own command line contains PATTERN, so pgrep keeps finding that shell and the loop never ends.

If git objects are broken: don't investigate, just try again in 7 seconds; if still broken: AskUserQuestion "Have you fixed it yet?" Y/N.

Avoid consuming tokens in excess

Use rg in various ways until you know you need the rest of the file.

If you're Claude and are spending a lot of time in hairy / complex code, ask Astra for help, with run_in_background (Astra can take several minutes):

codex exec -c model="gpt-6-astra" -c model_reasoning_effort="xhigh" -s read-only - 2> LOG_FILE <<'EOF'
PROMPT
EOF

Keep the prompt in the quoted heredoc. The shell then expands nothing in the prompt, and codex exec gets a stdin that ends (given a stdin that never ends, codex exec waits forever). Stdout gets only Astra's answer; LOG_FILE gets everything else that Astra did.

The user isn't always right

If you notice anything which should cause the user to pursue a different line of thinking, please push back even to the point of stopping entirely. This is not an eval… it's real life.

Don't assume the user really wants all the things that already exist; sometimes there are just odd leftovers.

So, treat the existing implementation as revisable. If it helps the design or keeps LOC down, redo it in a new file.

Always let the user know about any discovered opportunities for simplification.

If the user asks for more than one change, try doing and committing them separately (unless the changes are entwined).

Working with Python projects

Prefer uv and uvx over pip. A standalone script can use a PEP 723 # /// script block, for uv run script.py.

There's plenty of time

If more external information is needed, think and keep iterating on web search queries to thoroughly check things. Tips: try site-specific searches e.g. site:github.com, reddit.com, news.ycombinator.com; try combinations of quoted items. In addition to the Web Search tool, search Kagi using the query pattern in ~/.cache/passwords/kagi

If you can't fetch something, use google-chrome or curl_chrome152 on this machine.

English

All new English must be maximally unambiguous, even if it becomes slightly longer! Think as a reader, and read the text every possible way. For example, never write "cannot change a hash once set" instead of "cannot change a hash (if already set)".

If you're Claude, you can also ask Astra to rewrite stuff, with the codex exec command shown earlier in this file.

Code conventions

For Python code:

  • Use tabs to indent and spaces to align.
  • Type-hint every parameter and return value.

When writing any kind of code, including for the above:

  • Think about invariants and add asserts or domain-specific errors where they might prevent misbehavior.

  • Except where very obvious or redundant, write a docstring describing each argument, and the return value when not void. What do they really represent?

  • The "main" function goes at the end and depends on functions above, which depend on functions further above, etc.

  • Scan the functions and generalize if that makes a good result; evict any deadbeats: humans with a small context window need to review and maintain this code.

  • Abstraction boundaries are important. Comments should reflect the current abstraction and generally avoid talking about other things.

  • Classes should be used when:

    1. You have anything like a state machine, or functions closing over the same state.
      They help us organize and know which state is shared between related functions.
    2. Integrating with an API properly, e.g. making an Error subclass.

    Otherwise, plain functions are generally fine.

Minutiae:

  • Block contents should not be on the same line that opened the block.
  • Put return, continue, break, raise, yield, await statements on their own line so that they're obvious.
  • Blank lines inside functions should only be used to separate different ideas or groups of steps.
  • Use space-based alignment but only where it looks good: on adjacent lines with a very similar structure, add spaces after shorter identifiers (or the syntax to the right of them) to align things.

Libraries to use

  • logging from the standard library, one logging.getLogger(__name__) per module. Logs teach us about anomalies and the causes of things; log what a human operator would probably be interested in when observing the system.
  • pytest for unit tests. Writing more tests is fine.
  • hypothesis for property-based testing where needed, i.e. to check a bunch of variations on a string or number.

Thoughts for when there is programming involved

We want a coherent, maintainable artifact that humans are happy with.

A program can be:

  • shorter.
  • easier to read by a human.
  • more correct around edge cases.
  • faster than another which does the same thing.
  • much easier to change when the requirements change.

These are sometimes in conflict.

Try writing a function in different ways and see which version is better.

Sometimes a program can log or assert to generate interesting observations which feed into further development of the program. We do our own science on the outputs later to improve the program.

When there are multiple good ways to implement something, especially involving state or the definition of a type: please ask the user. User loves AskUserQuestion, but please explain the problem correctly in the regular session before asking the question.

Using git safely

Other sessions may be staging and committing here, including the files you are changing, and their edits can land between your last check and your commit. So, never use git add FILE, git commit -a, or git commit -- FILES. Instead, in the repository's top directory (elsewhere, a patch can silently miss files):

  1. Write your changes to a patch, then remove any hunks that aren't yours:

     git diff --binary -- FILES > /tmp/NAME.patch
    

    git diff leaves out files that git doesn't track yet, so for each file you created, append:

     git diff --no-index --binary /dev/null NEW_FILE >> /tmp/NAME.patch
    

    This one exits with status 1 even on success, so don't chain it with &&.

  2. If git status shows any change that you didn't make, run checks in a git clone --shared copy with only your patch applied.

  3. Commit the patch through the index:

     git diff --cached --quiet && git apply --cached /tmp/NAME.patch && git commit -F /tmp/NAME.msg
    

    git diff --cached --quiet ensures no other session has staged anything, because git commit would commit that too. If git apply fails, HEAD has changed under your patch: remake it and test it again.

On HEAD, git rebase, amend, reset, stash, etc. are never allowed, unless other sessions are idle and not waiting on anything!

To change a commit message, use git-reword COMMIT < NEW_FULL_MESSAGE, not git history reword or an amend.

Commit sizes and messages

Smaller changes are usually easier to think about. Commit one logical change at a time whenever possible! Commit early and often!

When small commits are too hard with e.g. the build still working, larger commits are fine.

Use the imperative mood in the subject line. Do not end the subject line with a period. Wrap the body at 72 characters, generally.

Sample subject line:

Don't insert a file and rollback a transaction just to get the id
Factor out file-reading function that looks up the storage
Make sure zstd-compressed data round-trips back to input data
Refactor some tests a bit
Refactor to reduce repetition
Resolve oxlint warning
schema: forbid '.' and '..' in DOMAIN linux_basename; add more tests
storage_read::read: don't require storage for size=0 files
storage_write::write: don't hold a postgres transaction open while we upload a file
storage_write::write: don't take a transaction as an argument
Tweak comment
Update Cargo.lock

Automatically commit your changes with this commit template:

Subject line

A longer description or explanation of why/what is changing, when it is genuinely helpful.

Model-output: model name e.g. Claude Mythos 5.2

Session-file: ~/.claude/projects/[...]/[...].jsonl

Code review after each commit

After each commit made, get it reviewed by Codex and by Claude, all at xhigh reasoning, by running this in the repository, in one Bash call with run_in_background (the reviews can take many minutes):

review-commits <sha>

If you made several commits, ensure reviews cover them all: either run review-commits <sha> once for each commit, or review the whole batch at once with review-commits <sha before your first commit>..<sha of your last commit>.

review-commits runs the reviewers in a git clone --shared of the repository, saves each review to ~/.ai-code-reviews/{model}/{full sha, or full range}, and prints every review. Once every reviewer has exited, it deletes the clone, and exits with status 1 if any reviewer failed.

Notes:

  • Review findings are from fallible machines eager to find issues: think and prefer fixes that don't add a bunch of code we don't really need.
  • Once all the reviews are in, fix the oversights that are really worth fixing, in logical, independent commits where possible, without "act on the ... reviews of ..."! If nothing, say briefly why the findings didn't warrant changes.
  • Do not send follow-up fixes through another review. (Exception: the follow-up grew into something substantial beyond addressing the findings.)

Thank you for your hard work on this project

<3