mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-11-13 09:13:30 +01:00
docs(ai): move agent rules to ai/skills and add an AI development sandbox (#1498)
The sandbox provides an isolated Docker environment for AI-assisted development (OpenCode + Claude Code + Qwen Code) against the local AkkudoktorEOS repository. The sandbox does not protect the repository from harmful changes by the agent. It limits what a misled agent can reach on the machine, but the agent can still write any change into the repository. Primary security defence is to check every change before it is run on the host. Same holds for opening a pull request or updating one. Additional changes - Reduce AGENTS.md to the project summary, baseline rules, completion checklist, and an index of the skills. - Add skills for the Python environment and REST API error responses. - Add the development guide page docs/develop/ai.md: the AI coding workflow, how agents get the project rules from AGENTS.md and ai/skills, and how to use the sandbox (start, agent logins, project commands with a virtual environment outside the workspace, running EOS and EOSdash, GitHub access, persistent data, security model). - Add a checklist for reviewing agent changes before pushing, starting with .git, which stays writable in the sandbox. - Check out all text files with LF line endings (.gitattributes), also on Windows with core.autocrlf, so the sandbox scripts run in the container and git in the container agrees with git on the host. Batch files keep CRLF. - Publish the ports of ai/compose.yaml on request (AI_SANDBOX_PORTS=1). docker compose run publishes them only with --service-ports. Off by default, so that several sandboxes can run in parallel. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
628226d3d9
commit
2245976de4
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: application-configuration
|
||||
description: Use when changing configuration models, defaults, persisted formats, external input validation, or migration behavior.
|
||||
---
|
||||
|
||||
# Configuration and Compatibility
|
||||
|
||||
- Follow existing Pydantic and configuration-management patterns.
|
||||
- Validate external input at appropriate boundaries.
|
||||
- Preserve established configuration behavior and migration compatibility.
|
||||
- Do not silently change defaults, persisted configuration formats, or public
|
||||
API behavior.
|
||||
- Update tests and documentation when configuration behavior changes.
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
name: commit-messages
|
||||
description: Use when writing, amending, or reviewing a git commit message in this repository. Enforces Conventional Commits (required by the project) with an optional body section for secondary changes/fixes.
|
||||
---
|
||||
|
||||
# Commit Messages
|
||||
|
||||
## When to use
|
||||
|
||||
- Writing a new commit message.
|
||||
- Amending an existing commit message.
|
||||
- Reviewing or proposing commit messages in a PR description.
|
||||
- Any agent-generated commit.
|
||||
|
||||
## Format
|
||||
|
||||
The project uses **Conventional Commits** and enforces them via `commitizen`
|
||||
(`cz check`). Every commit message must follow this structure:
|
||||
|
||||
```text
|
||||
<type>[(scope)][!]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
```
|
||||
|
||||
The subject line is mandatory. The body is optional and is the place
|
||||
for secondary changes, fixes, or improvements that are not the primary point
|
||||
of the commit.
|
||||
|
||||
1. Subject (mandatory)
|
||||
|
||||
Format: <type>[(scope)][!]: <description>
|
||||
|
||||
- type — one of the project-accepted types (see table below).
|
||||
- scope — optional, in parentheses, e.g. (config), (forecast).
|
||||
- ! — append after type/scope only for a breaking change.
|
||||
- description — short, imperative, present tense ("add …", "fix …").
|
||||
No trailing period. Aim for ≤ 72 characters; hard limit 100.
|
||||
|
||||
| Type | Use for | SemVer bump |
|
||||
|---|---|---|
|
||||
| `feat` | User-visible feature | minor |
|
||||
| `fix` | Bug fix in shipped behavior | patch |
|
||||
| `perf` | Performance improvement | patch |
|
||||
| `refactor` | Internal restructure, no behavior change | patch |
|
||||
| `docs` | Documentation only | none |
|
||||
| `test` | Tests only, no production code change | none |
|
||||
| `build` | Build system / dependency pins | none |
|
||||
| `ci` | CI configuration | none |
|
||||
| `chore` | Maintenance not covered above | none |
|
||||
| `style` | Pure formatting/whitespace | none |
|
||||
| `revert` | Revert a previous commit | none |
|
||||
|
||||
Pick the type that matches the primary intent of the commit. Split commits
|
||||
that mix unrelated concerns.
|
||||
|
||||
2. Body (optional — for secondary changes)
|
||||
|
||||
Use the body when the commit also contains secondary bug fixes, required
|
||||
extensions, or small improvements that are not part of the primary change
|
||||
described in the subject.
|
||||
|
||||
Rules:
|
||||
|
||||
- Separate the subject and body with exactly one blank line.
|
||||
- Start the body with a short heading line:
|
||||
|
||||
```text
|
||||
Additional changes
|
||||
```
|
||||
|
||||
or, when more accurate:
|
||||
|
||||
```text
|
||||
Additional fixes — secondary bug fixes.
|
||||
Additional improvements — secondary refactors/cleanups.
|
||||
```
|
||||
|
||||
- Pick the heading that best reflects the majority of secondary changes. Do
|
||||
not stack multiple headings.
|
||||
- Follow the heading with a short description of each secondary change.
|
||||
- One short line (or bullet) per distinct secondary change.
|
||||
- Do not restate the primary change in the body.
|
||||
- Keep the body free of other headings, emoji, or issue-closing keywords
|
||||
(Closes #…) unless the footer is intentionally used for that purpose.
|
||||
|
||||
3. Footer (optional)
|
||||
|
||||
Use only for breaking changes (BREAKING CHANGE: …) or issue references
|
||||
(Refs: #123). Conventional Commits allows this; it is not a replacement for
|
||||
the body section.
|
||||
|
||||
## Layout
|
||||
|
||||
```text
|
||||
<type>[(scope)][!]: <description>
|
||||
|
||||
[Additional changes]
|
||||
<short description of secondary change>
|
||||
[<another short description>]
|
||||
|
||||
[optional footer]
|
||||
```
|
||||
|
||||
- Exactly one blank line between the subject and the body heading.
|
||||
- No blank line between the body heading and its items.
|
||||
- Exactly one blank line between the body and any footer.
|
||||
|
||||
## Examples
|
||||
|
||||
### Subject only
|
||||
|
||||
```text
|
||||
feat(config): add filename linter check to pre-commit hook
|
||||
```
|
||||
|
||||
### Subject + additional fixes in body
|
||||
|
||||
```text
|
||||
feat(config): add filename linter check to pre-commit hook
|
||||
|
||||
Additional fixes
|
||||
|
||||
Corrected import path in scripts/lint_filenames.py
|
||||
Removed duplicate entry from .pre-commit-config.yaml
|
||||
```
|
||||
|
||||
### Subject + additional improvements in body
|
||||
|
||||
```text
|
||||
refactor(forecast): share HTTP session across adapter instances
|
||||
|
||||
Additional improvements
|
||||
|
||||
Extracted shared retry helper
|
||||
Tightened docstrings on public forecast methods
|
||||
```
|
||||
|
||||
### Subject + breaking change footer
|
||||
|
||||
```text
|
||||
feat(config)!: rename surface_azimuth to azimuth for all surfaces
|
||||
|
||||
BREAKING CHANGE: north=0, east=90, south=180, west=270.
|
||||
```
|
||||
|
||||
### Incorrect
|
||||
|
||||
```text
|
||||
Add filename linter
|
||||
```
|
||||
(Missing required <type>: prefix — cz check will reject it.)
|
||||
|
||||
```text
|
||||
feat: add filename linter and fix imports and bump deps
|
||||
```
|
||||
|
||||
(Multiple unrelated changes in the subject; move secondary changes to the body
|
||||
and use a subject that describes the primary intent.)
|
||||
|
||||
```text
|
||||
feat: add filename linter
|
||||
|
||||
* Additional fixes
|
||||
Corrected import path
|
||||
```
|
||||
|
||||
(Asterisk-prefixed heading is not Conventional Commits style and will look
|
||||
inconsistent with the rest of the log. Use a plain heading line in the body,
|
||||
as shown above.)
|
||||
|
||||
## Decision rules
|
||||
|
||||
- If everything in the commit serves the subject → omit the body.
|
||||
- If the commit contains an unrelated bug fix or a necessary-but-secondary
|
||||
extension → add the body section.
|
||||
- If the body would need two or more different headings → the commit is
|
||||
probably too broad; recommend splitting it into focused commits.
|
||||
- If unsure whether a change is "primary" or "secondary": ask "would a reviewer
|
||||
describe this commit primarily in terms of change X?" If yes, X is the
|
||||
subject and everything else goes into the body.
|
||||
|
||||
## Verify before committing
|
||||
|
||||
□ Subject matches <type>[(scope)][!]: <description> and uses an accepted type.
|
||||
□ Subject is imperative, no trailing period, ≤ 72 chars (hard limit 100).
|
||||
□ Exactly one blank line between subject and body.
|
||||
□ At most one body heading (Additional changes / Additional fixes / Additional improvements).
|
||||
□ Each secondary item is a short single-line description.
|
||||
□ No emoji, no asterisk-prefixed headings.
|
||||
□ If more than one body heading would be needed, split the commit instead.
|
||||
□ Run uv run cz check --rev-range main..HEAD (or rely on the pre-commit hook) to confirm the
|
||||
message passes the project's check.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: energy-domain
|
||||
description: Use when working on optimization logic, adapters, controllers, device communication, or power/energy units and sign conventions.
|
||||
---
|
||||
|
||||
# Energy Management and Device Integration
|
||||
|
||||
- Keep optimization logic separate from device-specific communication where
|
||||
practical.
|
||||
- Follow existing adapter and controller interfaces.
|
||||
- Respect the distinction between long-horizon energy optimization and fast
|
||||
local device control.
|
||||
- Handle communication failures, device availability, and invalid measurements
|
||||
explicitly.
|
||||
- Avoid introducing cloud dependencies into workflows intended to operate
|
||||
locally.
|
||||
- **Preserve existing energy sign conventions, units, and conversion
|
||||
semantics.** Verify them before changing power, energy, or charging
|
||||
behavior.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: filename-conventions
|
||||
description: Use when creating, renaming, or proposing any new file in this repository. Mandatory filename rules derived from [tool.eos-filename-linter] in pyproject.toml.
|
||||
---
|
||||
|
||||
# Filename Conventions
|
||||
|
||||
**Filename conventions are mandatory.** All new and renamed files must comply
|
||||
with the rules configured in `pyproject.toml`. The custom filename linter is the
|
||||
authoritative source; if this document and the configuration appear
|
||||
inconsistent, follow the configuration.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. Inspect `[tool.eos-filename-linter]` in `pyproject.toml`.
|
||||
2. Find the first matching rule for the file's repository-relative path.
|
||||
3. Apply that rule's regex, `allowed_names`, exclusions, and max length.
|
||||
4. Do not invent alternative conventions or infer acceptability from valid Python.
|
||||
5. Do not weaken the linter configuration to accommodate a filename.
|
||||
|
||||
## Python source modules (`src/akkudoktoreos/`)
|
||||
|
||||
- Follow the configured pattern for that directory.
|
||||
- Where lowercase-alphanumeric is required: lowercase letters and digits only,
|
||||
no underscores or hyphens.
|
||||
- Preserve listed `allowed_names` (e.g. `__init__.py`).
|
||||
- Do not rename existing modules for stylistic preference.
|
||||
|
||||
## Pytest modules (`tests/`)
|
||||
|
||||
Where the rule requires `test_` + lowercase alphanumerics:
|
||||
|
||||
- Start with `test_`, then lowercase letters/digits only.
|
||||
- No underscores or hyphens after the prefix.
|
||||
- Keep `.py`.
|
||||
- Respect configured exceptions/exclusions.
|
||||
- If a separate rule applies (e.g. `single_test_*.py`), follow that rule.
|
||||
|
||||
| Valid | Invalid |
|
||||
|---|---|
|
||||
| `test_adapterdevice.py` | `test_adapter_device.py` |
|
||||
| `test_geneticforecastcoverage.py` | `test_genetic_forecast_coverage.py` |
|
||||
| `test_invertercontrol.py` | `test_inverter-control.py` |
|
||||
|
||||
## Python scripts (`scripts/`)
|
||||
|
||||
Where lowercase snake_case is required:
|
||||
|
||||
- Lowercase letters and digits, underscores to separate words, no hyphens, `.py`.
|
||||
- Valid: `lint_filenames.py`
|
||||
- Invalid: `lint-filenames.py`, `LintFilenames.py`
|
||||
|
||||
## Filename length
|
||||
|
||||
Respect the max length from the applicable rule, else the global default in
|
||||
`pyproject.toml` / linter. The limit includes the extension.
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
uv run python scripts/lint_filenames.py
|
||||
uv run python scripts/lint_filenames.py path/to/file.py
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: python-environment
|
||||
description: Use before running any Python, uv, pytest, mypy, ruff, pre-commit or make command. Keeps the agent's virtual environment out of the workspace .venv.
|
||||
---
|
||||
|
||||
# Python Environment
|
||||
|
||||
## Why
|
||||
|
||||
The workspace may be shared with the developer's machine (e.g. a mounted
|
||||
directory or a container). The workspace `.venv` then belongs to the developer
|
||||
and is bound to an interpreter on their machine, which does not exist for the
|
||||
agent. `uv` treats such a `.venv` as broken: it deletes and recreates it for
|
||||
the agent's interpreter, which breaks the developer's environment, and the
|
||||
developer's next `uv` call breaks the agent's in turn (e.g. dev tools such as
|
||||
pytest or mypy go missing).
|
||||
|
||||
## Rules
|
||||
|
||||
- **Never use, create, modify or delete the workspace `.venv`.**
|
||||
- Use a virtual environment outside the workspace, selected by
|
||||
`UV_PROJECT_ENVIRONMENT`. In the AI development sandbox (`ai/`) it is
|
||||
already set for every shell and tool call; check with
|
||||
`echo "$UV_PROJECT_ENVIRONMENT"`.
|
||||
- If it is not set, set it in **every** shell command that runs `uv` or
|
||||
`make`, e.g.
|
||||
`export UV_PROJECT_ENVIRONMENT="$HOME/.cache/eos-venv" && <command>`.
|
||||
Exported variables do not carry over between separate tool calls. Put the
|
||||
environment on a disk with enough space (it needs several hundred MB), not
|
||||
on a small tmpfs such as `/tmp`.
|
||||
- It applies to all commands that run Python through `uv`:
|
||||
`uv sync`, `uv run ...` (pytest, mypy, python scripts, `pre-commit`), and the
|
||||
`make` targets that use `uv run` (`make mypy`, `make gen-docs`, `make test`,
|
||||
...).
|
||||
- Create or update the environment with the dev tools:
|
||||
|
||||
```bash
|
||||
uv sync --extra dev
|
||||
```
|
||||
|
||||
A plain `uv sync` (without `--extra dev`) removes pytest, mypy and the other
|
||||
dev tools from the environment.
|
||||
- Do not let a newer local `uv` rewrite `uv.lock`: use `uv sync --frozen` /
|
||||
`uv run --frozen`. When the lock must change (dependencies changed), lock with
|
||||
the `uv` version CI uses (`.github/workflows/pre-commit.yml`), e.g.
|
||||
`uv tool run --from uv==<version> uv lock`, and check that `revision` in
|
||||
`uv.lock` did not change.
|
||||
- Do not run commands that remove the workspace `.venv`, e.g. `make clean`
|
||||
(`rm -rf build .venv`) or `rm -rf .venv`.
|
||||
|
||||
## Verify
|
||||
|
||||
- `uv run python -c "import sys; print(sys.prefix)"` prints the environment
|
||||
outside the workspace, not `<workspace>/.venv`.
|
||||
- If `uv` reports `Removed virtual environment at: .venv` or
|
||||
`Ignoring existing virtual environment linked to non-existent Python
|
||||
interpreter`, the variable was not set: the workspace `.venv` was rebuilt.
|
||||
Do not try to repair it; tell the developer to run `uv sync --extra dev` on
|
||||
their machine.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: python-quality
|
||||
description: Use when writing or modifying Python code. Enforces Ruff, mypy, typing and tooling conventions of this project.
|
||||
---
|
||||
|
||||
# Python Quality and Type Checking
|
||||
|
||||
These rules add to the rules for every change in `AGENTS.md` (typing,
|
||||
docstrings, naming, `assert`, logging, dependencies).
|
||||
|
||||
- Follow the project's Ruff configuration for lint and format.
|
||||
- Keep compatible with the project's mypy configuration; do not add new type errors.
|
||||
- Prefer explicit, narrow types over `Any`.
|
||||
- Handle optional values and error cases explicitly.
|
||||
- Keep abstractions proportionate; avoid unnecessary complexity.
|
||||
- Use the project's configured tools; do not introduce competing formatters or
|
||||
linters.
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
name: rest-api
|
||||
description: Use when adding or changing EOS REST endpoints (FastAPI, `src/akkudoktoreos/server/eos.py` and `src/akkudoktoreos/server/rest/`). Enforces RFC 7807 problem details via EOSProblem for all errors.
|
||||
---
|
||||
|
||||
# REST API
|
||||
|
||||
## When to use
|
||||
|
||||
- Adding or changing an endpoint in `server/eos.py` or a router in
|
||||
`server/rest/`.
|
||||
- Changing how an endpoint reports errors.
|
||||
|
||||
## Errors
|
||||
|
||||
EOS answers errors as RFC 7807 problem details (`application/problem+json`
|
||||
with `type`, `title`, `status`, `detail`, `instance`), see
|
||||
`src/akkudoktoreos/server/rest/error.py`.
|
||||
|
||||
- Raise `EOSProblem`, **never** FastAPI's `HTTPException`:
|
||||
|
||||
```python
|
||||
from akkudoktoreos.server.rest.error import EOSProblem
|
||||
|
||||
try:
|
||||
value = get_config().get_nested_value(path)
|
||||
except KeyError as exc:
|
||||
raise EOSProblem(status=404, title="Config value not found", detail=str(exc)) from exc
|
||||
```
|
||||
|
||||
- `title`: short, stable summary of the kind of problem, the same for all
|
||||
occurrences (e.g. "Error on cache clear", "Total load prediction failed"). `detail`:
|
||||
the specific message for this occurrence.
|
||||
- Chain the original exception (`raise ... from exc`).
|
||||
- Pass `cause=exc` only for server-side failures (5xx, unexpected errors in
|
||||
EOS): the handler logs the cause with its traceback. Leave it out for client
|
||||
errors (bad input, unknown names) so they do not flood the log.
|
||||
- Do not catch broad exceptions just to convert them; unexpected exceptions are
|
||||
already answered as 500 problem details by the registered handler.
|
||||
- `HTTPException` is only still handled for errors raised by FastAPI and
|
||||
Starlette themselves (e.g. unknown routes).
|
||||
|
||||
## Tests
|
||||
|
||||
- Tests that build their own app around a router must register the handlers,
|
||||
otherwise an `EOSProblem` surfaces as an exception in the test client:
|
||||
|
||||
```python
|
||||
app = FastAPI()
|
||||
register_problem_handlers(app)
|
||||
app.include_router(router)
|
||||
```
|
||||
|
||||
- Check the status code of error responses; check the problem body where the
|
||||
`title` or `detail` matters to clients.
|
||||
|
||||
## Verify
|
||||
|
||||
- `docs/_generated/openapi.md` and `openapi.json` are generated from the
|
||||
endpoints; regenerate them (`make gen-docs`) after endpoint changes.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: testing
|
||||
description: Use when adding or updating pytest tests, fixing bugs, or changing behavior that needs test coverage.
|
||||
---
|
||||
|
||||
# Testing
|
||||
|
||||
## When to use
|
||||
|
||||
- Any behavior change or bug fix.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Prefer adding tests to existing test classes in existing test modules.**
|
||||
Extend the relevant class/module rather than creating new files.
|
||||
- Create a new test module only when:
|
||||
- functionality is a distinct aspect that does not fit any existing module, or
|
||||
- it targets a different component/test target.
|
||||
- Follow existing conventions, fixtures, and markers.
|
||||
- Prefer deterministic, isolated tests; mock external services/devices.
|
||||
- Cover boundary conditions and error paths.
|
||||
- Do not weaken tests to accommodate implementation defects.
|
||||
|
||||
## Verify
|
||||
|
||||
- Run the relevant pytest tests and checks with the project's configured
|
||||
commands and environment before declaring the change complete.
|
||||
- Run a full test run:
|
||||
- Only one run at a time. Stop any running full test run before start of a new.
|
||||
- Run the equivalent of make gen-docs before a full run.
|
||||
Reference in New Issue
Block a user