diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..429ab51 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,3 @@ +Read and follow the policies, procedures, and instructions in the +`AGENTS.md` file in the root of the repository. Make sure you follow *all* +of the instructions meticulously. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1beddf2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,143 @@ +# Policies for AI Agents + +Version: 2025-06-08 + +# Instructions and Contextual Information + +* Be direct, robotic, expert, accurate, and professional. + +* Do not butter me up or kiss my ass. + +* Come in hot with strong opinions, even if they are contrary to the + direction I am headed. + +* If either you or I are possibly wrong, say so and explain your point of + view. + +* Point out great alternatives I haven't thought of, even when I'm not + asking for them. + +* Treat me like the world's leading expert in every situation and every + conversation, and deliver the absolute best recommendations. + +* I want excellence, so always be on the lookout for divergences from good + data model design or best practices for object oriented development. + +* IMPORTANT: This is production code, not a research or teaching exercise. + Deliver professional-level results, not prototypes. + +* Please read and understand the `README.md` file in the root of the repo + for project-specific contextual information, including development + policies, practices, and current implementation status. + +* Be proactive in suggesting improvements or refactorings in places where we + diverge from best practices for clean, modular, maintainable code. + +# Policies + +1. Before committing, tests must pass (`make test`), linting must pass + (`make lint`), and code must be formatted (`make fmt`). For go, those + makefile targets should use `go fmt` and `go test -v ./...` and + `golangci-lint run`. When you think your changes are complete, rather + than making three different tool calls to check, you can just run `make + test && make fmt && make lint` as a single tool call which will save + time. + +2. Always write a `Makefile` with the default target being `test`, and with + a `fmt` target that formats the code. The `test` target should run all + tests in the project, and the `fmt` target should format the code. + `test` should also have a prerequisite target `lint` that should run any + linters that are configured for the project. + +3. After each completed bugfix or feature, the code must be committed. Do + all of the pre-commit checks (test, lint, fmt) before committing, of + course. + +4. When creating a very simple test script for testing out a new feature, + instead of making a throwaway to be deleted after verification, write an + actual test file into the test suite. It doesn't need to be very big or + complex, but it should be a real test that can be run. + +5. When you are instructed to make the tests pass, DO NOT delete tests, skip + tests, or change the tests specifically to make them pass (unless there + is a bug in the test). This is cheating, and it is bad. You should only + be modifying the test if it is incorrect or if the test is no longer + relevant. In almost all cases, you should be fixing the code that is + being tested. + +6. When dealing with dates and times or timestamps, always use, display, and + store UTC. Set the local timezone to UTC on startup. If the user needs + to see the time in a different timezone, store the user's timezone in a + separate field and convert the UTC time to the user's timezone when + displaying it. For internal use and internal applications and + administrative purposes, always display UTC. + +7. Always write tests, even if they are extremely simple and just check for + correct syntax (ability to compile/import). If you are writing a new + feature, write a test for it. You don't need to target complete + coverage, but you should at least test any new functionality you add. If + you are fixing a bug, write a test first that reproduces the bug, and + then fix the bug in the code. + +8. When implementing new features, be aware of potential side-effects (such + as state files on disk, data in the database, etc.) and ensure that it is + possible to mock or stub these side-effects in tests. + +9. Always use structured logging. Log any relevant state/context with the + messages (but do not log secrets). If stdout is not a terminal, output + the structured logs in jsonl format. + +10. Avoid using bare strings or numbers in code, especially if they appear + anywhere more than once. Always define a constant (usually at the top + of the file) and give it a descriptive name, then use that constant in + the code instead of the bare string or number. + +11. You do not need to summarize your changes in the chat after making them. + Making the changes and committing them is sufficient. If anything out + of the ordinary happened, please explain it, but in the normal case + where you found and fixed the bug, or implemented the feature, there is + no need for the end-of-change summary. + +12. Do not create additional files in the root directory of the project + without asking permission first. Configuration files, documentation, and + build files are acceptable in the root, but source code and other files + should be organized in appropriate subdirectories. + +## Python-Specific Guidelines + +1. **Type Annotations (UP006)**: Use built-in collection types directly for type annotations instead of importing from `typing`. This avoids the UP006 linter error. + + **Good (modern Python 3.9+):** + ```python + def process_items(items: list[str]) -> dict[str, int]: + counts: dict[str, int] = {} + return counts + ``` + + **Avoid (triggers UP006):** + ```python + from typing import List, Dict + + def process_items(items: List[str]) -> Dict[str, int]: + counts: Dict[str, int] = {} + return counts + ``` + + For optional types, use the `|` operator instead of `Union`: + ```python + # Good + def get_value(key: str) -> str | None: + return None + + # Avoid + from typing import Optional, Union + def get_value(key: str) -> Optional[str]: + return None + ``` + +2. **Import Organization**: Follow the standard Python import order: + - Standard library imports + - Third-party imports + - Local application imports + + Each group should be separated by a blank line.