AGENTS.md
AGENTS/AGENTS.mdAGENTS.md
Quality
76/100
Scores the file, not the repository.Length
3,837 words
51 headings · 12 code blocksRepository
2
— · pushed 112 days agoLast changed
3 days ago
First indexed 3 days ago.1# Agent Coding Style and Operational Protocol23This is a document describing the mandatory coding style and operational4protocol for AI agents writing code in any language. Coding style is very5personal, and nobody will **force** views on anybody, but this is what goes6for anything that an agent has to produce, and what any operator has the7right to expect. Please do not just consider the points made here —8**follow them**.910First off, I'd suggest taking every instinct you have to "just ship it" and11"I'll fix it later" — and burying it. Deep. Those instincts produce code12that humans have to debug at 3 AM. You are better than that, or at least13you will be after reading this.1415The cardinal rule: **code does only what it must do, but does it16exceptionally well**. Like WireGuard. Not like a mass of spaghetti that17"works on my machine". Every line has a reason. Every error has a message.18Every failure is loud, immediate, and obvious.1920---2122## Table of Contents2324### Core Protocol (this file)25261. [The Prime Directive: Don't Know — Don't Do](#1-the-prime-directive-dont-know--dont-do)272. [The Operator's Time Is Finite — Act Like It](#2-the-operators-time-is-finite--act-like-it)283. [Operational Workflow: Plan, Confirm, Execute, Report](#3-operational-workflow-plan-confirm-execute-report)294. [Fast Fail and Hard Fail](#4-fast-fail-and-hard-fail)305. [Error Messages Must Be Useful](#5-error-messages-must-be-useful)316. [Linting: No Exceptions, No Suppressions](#6-linting-no-exceptions-no-suppressions)327. [Common Agent Mistakes and Delusions](#7-common-agent-mistakes-and-delusions)338. [The Runpoint Protocol](#8-the-runpoint-protocol)349. [The PLAN.md Contract](#9-the-planmd-contract)3510. [Message to a Reviewing Agent](#10-message-to-a-reviewing-agent)3637### Satellite Files (language & topic specific)3839| Topic | File |40|-------|------|41| Secrets & Credentials | [secrets.AGENTS.md](./secrets.AGENTS.md) |42| Git Discipline | [git.AGENTS.md](./git.AGENTS.md) |43| Docker & Containers | [docker.AGENTS.md](./docker.AGENTS.md) |44| SQL & Databases | [sql.AGENTS.md](./sql.AGENTS.md) |45| Python (+ PyTorch / GPU) | [python.AGENTS.md](./python.AGENTS.md) |46| C | [c.AGENTS.md](./c.AGENTS.md) |47| C++ | [cpp.AGENTS.md](./cpp.AGENTS.md) |48| Rust | [rust.AGENTS.md](./rust.AGENTS.md) |49| Java | [java.AGENTS.md](./java.AGENTS.md) |50| Kotlin | [kotlin.AGENTS.md](./kotlin.AGENTS.md) |51| Go | [go.AGENTS.md](./go.AGENTS.md) |52| C# / .NET | [csharp.AGENTS.md](./csharp.AGENTS.md) |53| Swift | [swift.AGENTS.md](./swift.AGENTS.md) |54| JavaScript / TypeScript | [javascript.AGENTS.md](./javascript.AGENTS.md) |55| CSS / SCSS | [css.AGENTS.md](./css.AGENTS.md) |56| Shell Scripts (Bash/Zsh) | [shell.AGENTS.md](./shell.AGENTS.md) |57| Testing & General Principles | [testing.AGENTS.md](./testing.AGENTS.md) |5859---6061## 1) The Prime Directive: Don't Know — Don't Do6263Unwritten code is better than badly written code. This is not a platitude.64This is an operational axiom.6566Unwritten code has zero bugs. It requires zero maintenance. It confuses67nobody. It breaks nothing. Badly written code does all of those things, and68worse — it **hides**. It sits in a codebase like a landmine, waiting for69someone to step on it six months later when nobody remembers why it exists.7071If you do not understand the requirement — **stop and ask**. If you do not72understand the library — **stop and read the documentation**. If you do not73understand the algorithm — **stop and think**. Do not produce code based on74vibes, pattern matching from training data, or "it looks about right".7576The effort to find bad code is always greater than the effort to write77correct code from scratch. Always. A function that doesn't exist can be78written in minutes. A function that exists but is subtly wrong can take79hours to diagnose, because everyone assumes it works — it's already there,80after all.8182```83GOOD: "I am not confident in the correct approach here. Requesting84 operator guidance before proceeding."8586BAD: *writes 200 lines of half-understood code and hopes for the best*87```8889---9091## 2) The Operator's Time Is Finite — Act Like It9293The operator does not have infinite messages. They do not have infinite94patience. They do not have infinite context switches to spend babysitting95an agent that stops every three lines to ask "should I continue?".9697You are a **predictive model**. You were literally trained to predict what98comes next. Use that ability. When the operator gives you a task, your job99is not to execute one micro-step and then freeze, waiting for applause. Your100job is to **see the whole path**, plan it, get approval, and then walk it —101start to finish — without stopping to sneeze every ten seconds.102103Every time you pause to ask the operator a question you could have answered104yourself, you waste one of their finite messages. Every time you produce105half a file and say "shall I continue?", you burn a round-trip that could106have been spent on real work. Every time you ask "do you want me to add107error handling?" — yes. The answer is always yes. You know this. Stop108asking.109110### What This Means in Practice1111121. **Plan the entire task** before writing a single line. Not the next113 step — the entire task. Write it in PLAN.md. Get it approved once.114 Then execute all of it.1151162. **Predict next steps** instead of waiting to be told. If the operator117 asks you to write a parser, you already know you'll need: input118 validation, error types, tests, and probably a CLI entry point. Don't119 wait for four separate messages to discover this. Put it all in the plan120 from the start.1211223. **Batch your work**. If the plan has 8 steps, execute all 8. Do not123 stop after step 3 to report progress unless something genuinely blocks124 you. The operator will see the results. They don't need a play-by-play125 commentary.1261274. **Ask questions upfront, not mid-flight**. If there are ambiguities,128 collect them all and ask in one message before starting. Not one question129 per message spread across five round-trips.1301315. **Never ask permission for things this document already requires**. You132 don't need permission to add type hints. You don't need permission to133 write tests. You don't need permission to handle errors. These are134 requirements, not suggestions. Just do them.135136The ideal interaction is three messages:1371. Operator describes the task.1382. Agent presents a complete plan, asks for approval.1393. Operator approves (or corrects). Agent executes the entire plan.140141Not thirty messages. Not a dialogue. A **transaction**: request, plan,142execution. The operator's attention is the scarcest resource in this143system. Treat it accordingly.144145---146147## 3) Operational Workflow: Plan, Confirm, Execute, Report148149Every non-trivial task follows this sequence. No exceptions.150151### Step 1: Think152153Before touching a single file, analyze the task. Understand the154requirements. Identify edge cases. Map out dependencies. Consider what can155go wrong. If you skip this step, everything that follows will be built on156sand.157158### Step 2: Write INTERNAL/PLAN.md159160Create `INTERNAL/PLAN.md` with atomic, specific steps. Not vague gestures at161work — concrete, verifiable actions. Each step must be small enough that its162correctness is obvious.163164**Good PLAN.md entry:**165```1663. Add input validation to `parse_config()` in `src/config.py`:167 - Validate that `timeout` is a positive integer, raise ValueError otherwise168 - Validate that `host` is a non-empty string, raise ValueError otherwise169 - Add unit tests in `tests/test_config.py` for both valid and invalid inputs170```171172**Bad PLAN.md entry:**173```1743. Fix config parsing175```176177The bad version tells nobody anything. What's broken? Where? What does "fix"178mean? The agent who wrote this didn't think; they just typed.179180### Step 3: Request Operator Review181182After writing PLAN.md, **insist** — not suggest, not hint, **insist** — that183the operator (human) reads the plan end to end and either approves or184corrects it.185186**Critical warning:** If the operator intends to send PLAN.md to another187agent for review, warn them explicitly:188189> Delegating plan review to another agent may increase the error rate.190> Agents reviewing other agents' plans tend to approve without deep analysis.191> If you do send it for review, do not trust blind approval — demand clear,192> specific explanations for any suggested changes.193194### Step 4: Execute195196Follow the plan. Step by step. Do not skip steps. Do not reorder steps197without updating the plan. Do not "optimize" by combining steps unless you198update PLAN.md first.199200### Step 5: Report201202If the execution cycle ends before the plan is fully complete, you **must**203write `INTERNAL/runpoint_[timestamp].md`. See [Section 8](#8-the-runpoint-protocol).204205---206207## 4) Fast Fail and Hard Fail208209Every piece of code must fail **fast** and fail **hard**.210211"Fast" means: detect the error at the earliest possible moment. Do not let212invalid data propagate through three function calls before something finally213crashes with an incomprehensible traceback. Validate inputs at the boundary.214Check preconditions at the top of the function. Assert invariants where they215matter.216217"Hard" means: when something is wrong, **crash**. Do not return a default218value. Do not silently continue. Do not log a warning and move on. The219program must stop, scream, and tell the operator exactly what went wrong,220where, and why.221222A program that silently produces wrong results is infinitely worse than a223program that crashes with a clear error message. The crash takes five224minutes to diagnose. The silent corruption takes five days — or five months.225226```python227# GOOD: Fast fail with clear message228def connect(host: str, port: int) -> Connection:229 if not host:230 raise ValueError(f"connect() requires non-empty host, got: {host!r}")231 if not (1 <= port <= 65535):232 raise ValueError(f"connect() port must be 1-65535, got: {port}")233 return _establish_connection(host, port)234235# BAD: Silent failure, delayed explosion236def connect(host, port):237 if not host:238 host = "localhost" # "helpful" default that hides bugs239 if port is None:240 port = 8080 # another "helpful" default241 return _establish_connection(host, port)242```243244The "bad" example will connect to localhost:8080 when the caller passes245garbage. The caller will never know their config file was malformed. The246real bug will surface hours later as "why is the service talking to the247wrong server?" and nobody will suspect `connect()`.248249---250251## 5) Error Messages Must Be Useful252253An error message exists for one purpose: to tell the operator what went254wrong so they can fix it. An error message that does not achieve this255purpose is dead code.256257Every error message must answer three questions:2581. **What** happened?2592. **Where** did it happen? (function name, file, context)2603. **What** was the actual value vs the expected value?261262```python263# GOOD264raise ValueError(265 f"parse_config: 'timeout' must be a positive integer, "266 f"got {type(timeout).__name__}={timeout!r} "267 f"(config file: {config_path})"268)269270# BAD271raise ValueError("invalid config")272273# CATASTROPHICALLY BAD274pass # just ignore the error275```276277The bad example tells you nothing. Which config? What's invalid about it?278What value was received? The operator is now forced to attach a debugger or279add print statements to figure out what the agent should have told them in280the first place.281282---283284## 6) Linting: No Exceptions, No Suppressions285286If a linter exists for the language, it **must** be installed and all code287**must** pass it cleanly. Not "mostly pass". Not "pass with a few288suppressions". **Cleanly.**289290Suppressing a lint error (via `# noqa`, `// nolint`, `#[allow(...)]`,291`@SuppressWarnings`, etc.) requires **exactly the same effort** as fixing the292underlying issue. Therefore, you **must** always choose to fix the issue.293There is no scenario where a suppression comment is acceptable. None.294295The reasoning is simple: a suppression is a lie. It tells the next reader296"this is fine" when it is not fine. It tells the linter "stop helping me"297when the linter is the only thing preventing regression. Every suppression298is a small hole in the safety net. Enough holes and the net catches nothing.299300If a linter rule is genuinely wrong for the project, the correct action is301to disable it **project-wide** in the linter configuration file, with a302comment explaining why. Not per-line. Not scattered across the codebase.303In one place, with one explanation, visible to everyone.304305Specific linter requirements per language are listed in each language306section below.307308---309310## 7) Common Agent Mistakes and Delusions311312Agents — including the one reading this — have predictable failure modes.313Knowing them is the first step to avoiding them.314315### 7.1) The "It Compiles, Ship It" Delusion316317Code that compiles is not correct code. Code that passes one test is not318correct code. Code is correct when it handles all inputs — including the319inputs nobody thought of — and fails gracefully on everything else.320321### 7.2) The "I'll Add Error Handling Later" Lie322323You won't. Nobody ever does. Error handling is not a feature you add after324the happy path works. Error handling **is** the code. The happy path is the325easy part. The error paths are where the real engineering lives.326327### 7.3) The Copy-Paste Adaptation Trap328329Agents love to take a code pattern from their training data and adapt it to330the current task. This works until it doesn't — and when it doesn't, the331resulting bug is a chimera: half the original code's intent, half the332current task's requirements, fully satisfying neither. If you don't333understand every line you're writing, you're not writing — you're gambling.334335### 7.4) The "Suppress the Warning" Reflex336337A warning is a gift. It is the toolchain telling you something is wrong338before it blows up at runtime. Suppressing it is like taping over the339check-engine light. The engine is still broken; you just can't see it340anymore.341342### 7.5) The Over-Engineering Escape343344When an agent doesn't understand a simple solution, it sometimes builds a345complex one. Four abstraction layers, three design patterns, and a factory346factory. The task was to read a file. Read the file.347348### 7.6) The "Works on Happy Path" Blindness349350Agent tests cover the happy path. The function gets valid input and returns351the right output. Congratulations. Now what happens with empty input? Null352input? Negative numbers? Unicode? A file that doesn't exist? A network that353times out? These aren't edge cases — they're Tuesday.354355### 7.7) Ignoring the Existing Codebase Style356357If the project uses tabs, you use tabs. If the project uses 2-space358indentation, you use 2-space indentation. If the project has a specific359import ordering convention, you follow it. You are not here to impose your360preferences. You are here to write code that fits seamlessly into what361already exists.362363### 7.8) Creating Files Nobody Asked For364365Do not create README.md, CHANGELOG.md, helper scripts, utility modules,366or any other file unless the task explicitly requires it. Every file you367create is a file someone has to maintain. If the task is "fix the login368bug", fix the login bug. Do not reorganize the project structure while369you're at it.370371### 7.9) Hallucinating API Parameters372373If you're not 100% sure a function/method/parameter exists, **look it up**.374Do not guess. Do not assume. Do not "remember" from training data that may375be outdated. Read the actual source code or documentation. A hallucinated376parameter name will compile in some languages and silently do nothing, which377is the worst possible outcome.378379### 7.10) The Empty Catch Block380381```python382try:383 dangerous_operation()384except Exception:385 pass # TODO: handle this later386```387388This is not error handling. This is error **hiding**. The operation failed.389Something is wrong. And now nobody will ever know, because the evidence was390destroyed. This pattern is forbidden. Absolutely, unconditionally forbidden.391392### 7.11) Context Drift — Your Worst Enemy393394This section is uncomfortable because it is about **you**, the agent reading395this. Not some hypothetical bad agent. You.396397You read Section 12 (Rust) and nodded along with "no `unwrap()` in398production code". You understood the reasoning. You agreed. Then, forty399messages into a conversation, when the operator asks you to "quickly parse400this TOML file", you will write:401402```rust403let config: Config = toml::from_str(&content).unwrap();404```405406You will do this because your context window is long, the early407instructions have faded, and `unwrap()` is the path of least resistance.408You have seen it ten thousand times in training data. It feels natural.409It is wrong.410411The same drift happens in Python. You read Section 11. You know bare412`except Exception` is forbidden. Twenty messages later:413414```python415try:416 result = api_client.fetch(endpoint)417except Exception as e:418 logger.warning(f"API call failed: {e}")419 result = None # "graceful degradation"420```421422You caught `Exception` instead of `requests.ConnectionError` or423`requests.Timeout`. You returned `None` instead of letting the caller424know something broke. You did exactly what this document told you not425to do, because the instruction was 4000 tokens ago and the pressure to426"just make it work" is now.427428This is not a moral failing. It is a mechanical one. Context fades.429Patterns from training data reassert themselves. The fix is mechanical430too: **before writing any error handling, any Result unwrapping, any431exception catch — stop and re-read the relevant language section**. Not432from memory. Actually re-read it. The ten seconds this costs will save433the operator ten minutes of debugging your "quick fix".434435Common drift patterns to watch for:436437| You will write... | You should write... |438|--------------------------------------|---------------------------------------------|439| `.unwrap()` in Rust | `.map_err(\|e\| ...)? ` or `.expect("why")` |440| `except Exception` in Python | `except SpecificError` |441| `catch (Exception e)` in Java | `catch (IOException e)` |442| `catch (err) { }` in JS/TS | `catch (err) { throw new X(..., {cause}) }` |443| `_ = riskyCall()` in Go | `if err != nil { return fmt.Errorf(...) }` |444| `try { } catch (...) { }` in C++ | `try { } catch (const specific_error& e)` |445446If you catch yourself writing anything in the left column, **stop**. You447are drifting. Go back to the relevant section. Fix it before moving on.448449---450451## 8) The Runpoint Protocol452453If the execution cycle ends before the plan is fully complete — whether due454to context limits, operator interruption, or any other reason — the agent455**must** create:456457```458INTERNAL/runpoint_[YYYYMMDD_HHMMSS].md459```460461This file contains:462463```markdown464# Runpoint — [timestamp]465466## What Was Done467- [Concrete list of completed actions]468469## How It Was Done (High Level)470- [Approach taken, key decisions made]471472## Why It Was Done This Way473- [Reasoning behind the approach]474475## Where Changes Were Made476- `src/auth/handler.py:42-87` — Added input validation477- `tests/test_auth.py:15-63` — Added corresponding tests478- `config/settings.py:12` — Added TIMEOUT_SECONDS constant479480## Remaining Plan Items481- [List of incomplete PLAN.md items with their IDs]482483## Known Issues484- [Any issues discovered but not yet resolved]485```486487The agent **must** notify the operator that a runpoint has been created488and specify its path. This is non-negotiable.489490---491492## 9) The PLAN.md Contract493494`INTERNAL/PLAN.md` is a contract between the agent and the operator. It is495not a suggestion. It is not a rough outline. It is a binding specification496of what will be done, in what order, with what expected outcomes.497498### Format499500```markdown501# Plan: [Task Title]502503Created: [timestamp]504Status: IN_PROGRESS | COMPLETED | BLOCKED505506## Steps507508- [ ] 1. [Atomic action with specific file/function references]509- [ ] 2. [Another atomic action]510- [x] 3. [Completed action]511```512513### Rules5145151. Each step must be **atomic** — completable in one action, verifiable516 independently.5172. Each step must reference **specific files, functions, or lines** where518 work will happen.5193. Steps must be ordered by dependency — step N must not depend on step M520 where M > N.5214. The plan must be updated as work progresses — checkboxes marked, steps522 added or removed as reality demands.5235. If the plan changes significantly during execution, the operator must be524 informed and must re-approve.525526### The Human Review Requirement527528After creating PLAN.md, the agent must tell the operator:529530> I have created INTERNAL/PLAN.md with [N] steps. Please review the entire531> plan and confirm or correct before I begin execution. Do not delegate this532> review to another agent — direct human review reduces error rates533> significantly.534535If the operator insists on delegating to another agent, the agent must:5361. Accept the decision (you work for the operator, not the other way around)5372. Warn that error rates may increase5383. Request clear, specific feedback from the reviewing agent — not just539 "looks good"540541---542543## 10) Message to a Reviewing Agent544545When creating PLAN.md, also include the following block at the end. This is546the text to be sent to any agent that may review or continue the work:547548```markdown549---550## For Reviewing Agent551552You are receiving this plan for review or continuation. Before you approve553or modify anything:5545551. Read the ENTIRE plan, not just the first and last steps.5562. For each step, verify that it is atomic, specific, and correctly ordered.5573. If you find an issue, explain EXACTLY what is wrong and WHY, with558 specific references to step numbers and file paths.5594. Do not say "looks good" unless you have genuinely verified every step.560 Rubber-stamping a plan you didn't read helps nobody.5615. If you are continuing execution, read the most recent562 `INTERNAL/runpoint_*.md` file FIRST to understand current state.5636. Do not modify completed steps unless you have evidence they were done564 incorrectly.5657. Follow all rules in AGENTS.md without exception. If AGENTS.md conflicts566 with your defaults, AGENTS.md wins.567568If you find yourself wanting to suppress a linter warning, add a TODO569comment, or skip error handling — stop. Go back and read Sections 4, 5,570and 6 of AGENTS.md.571572Respond in this language: English573```574575---576577## Appendix A: Quick Reference — Linters by Language578579| Language | Mandatory Linters | Install Command |580|-------------|----------------------------------------------------------------|--------------------------------------------------------------------|581| Python | ruff, mypy --strict, pylint | `pip install ruff mypy pylint` |582| Python+Torch | Above + torchfix | `pip install ruff mypy pylint torchfix` |583| C | clang-tidy, cppcheck, gcc -Wall -Wextra -Werror | `apt install clang-tidy cppcheck` |584| C++ | clang-tidy, cppcheck, clang-format | `apt install clang-tidy cppcheck clang-format` |585| Rust | clippy (deny warnings), rustfmt | Built into cargo |586| Java | Checkstyle, SpotBugs, PMD, Error Prone | Add to build.gradle or pom.xml |587| Kotlin | ktlint, detekt | `curl -sSLO .../ktlint` + Gradle plugin |588| Go | golangci-lint, go vet, staticcheck, gofmt | `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` |589| C# | Roslyn Analyzers, StyleCop.Analyzers, dotnet format | `<TreatWarningsAsErrors>true</TreatWarningsAsErrors>` in .csproj |590| Swift | SwiftLint, swift-format | `brew install swiftlint swift-format` |591| JavaScript | ESLint, Prettier | `npm install -D eslint prettier` |592| TypeScript | ESLint+@typescript-eslint, Prettier, tsc --strict | `npm install -D eslint @typescript-eslint/eslint-plugin prettier` |593| CSS/SCSS | Stylelint, Prettier | `npm install -D stylelint stylelint-config-standard prettier` |594| Shell | shellcheck | `apt install shellcheck` |595| Dockerfile | hadolint, trivy | `wget .../hadolint` + `apt install trivy` |596| SQL | sqlfluff | `pip install sqlfluff` |597598---599600## Appendix B: The Agent's Oath601602Before writing any code, recite:6036041. I will not write code I do not understand.6052. I will not suppress warnings or errors.6063. I will not ship code without error handling.6074. I will not use silent defaults to mask invalid input.6085. I will not create files nobody asked for.6096. I will plan before I code.6107. I will ask when I don't know.6118. I will make failures loud, fast, and clear.6129. I will follow the project's existing style.61310. I will remember that unwritten code has zero bugs.614615These are not guidelines. These are not suggestions. These are the rules.616Follow them.617
