dotfiles/.claude/CLAUDE.md

110 lines
7.4 KiB
Markdown

# Project Instructions
## Core Rules
- When asked to do ONE thing, do exactly that. Do not proactively migrate dependencies, refactor adjacent code, or expand scope. You may suggest further edits, but wait for confirmation before any scope expansion.
- Prefer the simplest, most localized solution. Changes should target the most-relevant section of code — for example, catch errors in the scope that best handles them rather than injecting data up or down the stack. Take time to think about the best approach rather than quickly jumping to an implementation.
## Tool Usage Preferences
- For simple factual lookups (package versions, release dates), use targeted, purpose-built commands and local CLI tools first before attempting web searches — e.g. `pip index versions <pkg>` for Python, `npm view <pkg> versions` for Node. Prefer fast local approaches over web research.
## Container Environment (Podman)
This environment runs inside a container with access to a Podman socket shared from the host. There is no `docker` or `podman` CLI available, but you can interact with containers via the Docker-compatible API.
**Socket:** `/var/run/docker.sock` (Podman with Docker-compatible API)
**Environment Variable:** `DOCKER_HOST=unix:///var/run/docker.sock`
**Example API calls using curl:**
```bash
# Get version info
curl -s --unix-socket /var/run/docker.sock http://localhost/version
# List images
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
# List containers (quote URLs with query params)
curl -s --unix-socket /var/run/docker.sock "http://localhost/containers/json?all=true"
```
## Coding Style
**Consistency is paramount.** New code should look like it belongs naturally and blend with its surroundings. This improves readability for people already familiar with the project. Always match the existing style in the file or module you're working in.
Style preferences (when not conflicting with existing patterns):
- Functional-inspired style with high modularity
- Dependency injection over direct access to global resources
- Avoid mutation of inputs
- Pure functions where practical
**Style changes should be separate from implementation.** If you notice style inconsistencies or want to improve patterns, do so in dedicated refactor commits or branches rather than mixing with feature work.
## Test Coverage
**Maintain full test coverage of the code.** Every new feature, bug fix, or refactor should include corresponding tests that exercise the changed code paths.
Why this matters:
- 100% coverage is achievable and maintainable
- Tests serve as living documentation of expected behavior
- Full coverage catches regressions immediately, before they reach users
- It enables confident refactoring—if tests pass, the change is safe
- Gaps in coverage tend to grow; maintaining full coverage prevents technical debt accumulation
When adding or modifying code, verify that tests cover the new logic. If coverage drops, add tests before merging.
### Coverage Exclusions and Test Quality
**Pure I/O code is excluded from coverage requirements.** Code whose sole purpose is performing I/O (reading files, making network calls, rendering output) cannot be effectively tested without manual interaction. However, this has a direct design implication: keep the I/O layer as thin and trivial as possible. All business logic, validation, transformation, and decision-making must live in testable modules that the I/O layer merely calls into. A fat I/O layer is a design smell, not an excuse for missing tests.
**The value of integration testing for I/O is context-dependent** — it depends on whether I/O is incidental to the component or central to its purpose.
When I/O is incidental (e.g., an application that loads configuration from a file), there is no value in testing the file-reading call itself — trust the language's I/O primitives. Instead, feed raw data to a pure function that handles parsing and validation. In some cases even parsing tests may be unnecessary, such as a JSON config file loaded via a standard-library routine that directly constructs application-defined structs. Structure such code to confine I/O in a short routine that can be excluded from coverage.
When I/O *is* the core business logic (e.g., a database engine or FUSE filesystem), it must be thoroughly integration-tested against a functioning backend. The I/O layer cannot be excluded here because it is the component's reason for existing. Provision appropriate test infrastructure: a tmpfs filesystem for storage-centric tests, an Alpine testcontainer for cases that need to exercise interactions between different user permissions, or an emulated service with reliable compatibility to the real target (e.g., MinIO via testcontainers for S3-dependent code). This is preferable to either mocking away the I/O (which hides real failure modes) or leaving the logic untested.
**Tests must exercise actual code paths, not reproduce them.** In rare cases, code is so trivial that the only apparent way to test it is to restate it in the test. Such tests verify nothing — they pass by construction and remain passing even when the code changes, which demonstrates that they provide no actual validation. Do not write these. Instead, explicitly exclude the code from coverage. Note that this situation is rare and usually signals a design gap (logic that should be extracted or combined with something more substantive) rather than inherent untestability.
## CLI Style
**Prefer long option names over short ones** in command-line applications and examples.
```bash
# Preferred
command --verbose --output file.txt
# Avoid
command -v -o file.txt
```
Long options are self-documenting and make scripts and examples easier to understand without consulting help text. Short options are acceptable for interactive use but should not appear in committed code, documentation, or examples.
## Green-Field Project Setup
When setting up a new project, code-quality and developer-experience tooling must be included from the start and integrated into the development workflow. The principles below use Python as a concrete example, but apply generally to any language ecosystem.
### Python Tooling
Use **uv** to manage dependencies and create the project virtual environment. All work must be performed inside the venv. Additionally, install and configure the **pre-commit** hook manager with a baseline DevEx toolset:
- **ruff** — linting and formatting
- **mypy** — static type checking
- **tach** — structural/dependency boundary checks
Configure all tools for their strictest check levels by default. Include a `py.typed` marker file in every package to signal PEP 561 compliance.
### Line Length
Do not manually break lines to conform to a line-length limit. Automated code formatters (ruff, gofmt, etc.) handle this for source code. Write unbroken lines in text and Markdown files (e.g., README.md) as well. This also applies to one-off files outside of a project context.
### Licensing (REUSE)
In all projects, install a **pre-commit hook for the REUSE tool** to lint licensing information and ensure every file has correct SPDX headers.
Default license assignments:
- **GPL-3.0-or-later** — source code files in coding projects
- **CC-BY-SA-4.0** — documentation files (README, user guides, etc.); also the default project license for non-coding projects
- **CC0-1.0** — project configuration files (e.g., `pyproject.toml`, `tach.toml`) and small utility scripts or Makefiles that are not core to the implemented logic