agent: Minimize the amount of instructions in AGENTS.md

Let's only keep instructions for stuff that we've seen the AI
mess up in practice rather than having a bunch of AI generated
text that it can figure out for itself these days (given it was
trained on systemd's source code in the first place).

Also add a rule to use git worktrees and check out PRs locally when
reviewing them, since I've seen it mess that up in practice.
This commit is contained in:
Daan De Meyer
2026-03-06 10:20:25 +01:00
parent f0691ad697
commit 83e872b4c2
2 changed files with 13 additions and 147 deletions
+12 -147
View File
@@ -1,10 +1,7 @@
# AGENTS.md
This file provides guidance to AI coding agents when working with code in this repository.
## Project Overview
systemd is a system and service manager for Linux, written in C (GNU17 with extensions). The project is built with Meson and consists of ~140 components including PID 1, journald, udevd, networkd, and many other system daemons.
This file provides guidance to AI coding agents when working with code in this repository. Only add
instructions to this file if you've seen an AI agent mess up that particular bit of logic in practice.
## Key Documentation
@@ -17,154 +14,22 @@ Always consult these files as needed:
## Build and Test Commands
**CRITICAL: Read and follow these instructions exactly.**
- Never compile individual files or targets. Always run `mkosi -f box -- meson compile -C build` to build
the entire project. Meson handles incremental compilation automatically.
- Never run `meson compile` followed by `meson test` as separate steps. Always run
`mkosi -f box -- meson test -C build -v <TEST-NAME>` directly. Meson will automatically rebuild any required
targets before running tests.
- Never invent your own build commands or try to optimize the build process.
- Never use `head`, `tail`, or pipe (`|`) the output of build or test commands. Always let the full output
display. This is critical for diagnosing build and test failures.
- **NEVER** compile individual files or targets. **ALWAYS** run `mkosi -f box -- meson compile -C build` to build the entire project. Meson handles incremental compilation automatically.
- **NEVER** run `meson compile` followed by `meson test` as separate steps. **ALWAYS** run `mkosi -f box -- meson test -C build -v <TEST-NAME>` directly. Meson will automatically rebuild any required targets before running tests.
- **NEVER** invent your own build commands or try to optimize the build process.
- **NEVER** use `head`, `tail`, or pipe (`|`) the output of build or test commands. Always let the full output display. This is critical for diagnosing build and test failures.
## Integration Tests
```sh
# Initial setup (one-time)
mkosi -f genkey
mkosi -f box -- meson setup build
# Build everything
mkosi -f box -- meson compile -C build
# Run a specific unit test (also rebuilds as needed)
mkosi -f box -- meson test -C build -v <TEST-NAME>
# Run all unit tests
mkosi -f box -- meson test -C build --print-errorlogs -q
# Build and boot an OS image for integration testing
mkosi -f box -- meson compile -C build mkosi
mkosi boot # nspawn
mkosi vm # qemu
```
- **CORRECT**: `mkosi -f box -- meson test -C build -v <TEST-NAME>`
- **WRONG**: Separate compile then test steps
- **WRONG**: `mkosi -f box -- meson compile -C build src/core/systemd` (individual target)
- **WRONG**: `mkosi -f box -- meson test -C build -v <TEST-NAME> 2>&1 | tail -100` (piped output)
## Code Architecture
### Shared Code Dependency Hierarchy (strict layering)
```
src/fundamental/ → no dependencies (used in EFI + userspace)
↑
src/basic/ → depends only on fundamental (userspace only)
↑
src/libsystemd/ → depends on basic + fundamental (public libsystemd.so)
↑
src/shared/ → depends on all above (libsystemd-shared-<nnn>.so)
↑
src/<component>/ → individual daemons and tools
```
Code should be linked as few times as possible. Place shared code at the lowest possible layer.
### Key Components
- `src/core/` — PID 1 service manager (system and user instances). Uses `systemd-executor` for process spawning via `posix_spawn()` to avoid fork+exec pitfalls.
- `src/udev/` — udev daemon and udevadm tool
- `src/journal/` — journald logging daemon
- `src/network/` — networkd network manager
- `src/resolve/` — resolved DNS resolver
- `src/login/` — logind session manager
- `src/boot/` — systemd-boot EFI bootloader
- `src/systemctl/`, `src/journalctl/`, `src/analyze/` — CLI tools
### Unit Settings Implementation
Adding a new unit setting requires changes in various places:
1. `src/core/load-fragment-gperf.gperf.in` + `src/core/load-fragment.c` — unit file parsing
1. `src/core/dbus-*.c` — D-Bus interface
1. `src/core/varlink-*.c` — Varlink interface
1. `src/shared/bus-unit-util.c` — client-side parsing for systemctl/systemd-run
1. `test/fuzz/fuzz-unit-file/` — add to fuzz corpus
### Tests
- **Unit tests**: `src/test/` — match source files (e.g., `src/test/test-path-util.c` tests `src/basic/path-util.c`). Use assertion macros from `tests.h` (`ASSERT_GE()`, `ASSERT_OK()`, `ASSERT_OK_ERRNO()` for glibc calls).
- **Fuzzers**: `src/fuzz/` for basic/shared code; next to component code otherwise. Input samples in `test/fuzz/`.
- **Integration tests**: `test/TEST-*` directories, run via systemd-nspawn or qemu.
## Coding Style (Essential Rules)
The full style guide is in `docs/CODING_STYLE.md`. Key rules:
### Formatting
- 8-space indent (no tabs) for C; 4-space for shell scripts; 2-space for man pages (XML)
- ~109 character line limit
- Opening brace on same line: `void foo() {`
- Function parameters with double indent (16 spaces) when broken across lines
- Single-line `if` blocks without braces
- `/* comments */` for committed code; `//` reserved for temporary debug comments
- Pointer in return types: `const char* foo()` (star with type)
- Pointer in parameters: `const char *input` (star with name)
- Casts: `(const char*) s` (space before `s`, not after `*`)
### Naming and Structure
- Structs: `PascalCase`; variables/functions: `snake_case`
- Return parameters prefixed `ret_` (success) or `reterr_` (failure)
- Command-line globals prefixed `arg_`
- Enum flags: use `1 << N` expressions, aligned vertically
- Non-flag enums: include `_FOO_MAX` and `_FOO_INVALID = -EINVAL` sentinels
- Commit messages: prefix with component name (e.g., `journal: `, `nspawn: `)
### Error Handling
- Return negative errno values: `return -EINVAL`
- Use `RET_NERRNO()` to convert libc-style returns
- Combined log+return: `return log_error_errno(r, "Failed to ...: %m")`
- Use `SYNTHETIC_ERRNO()` for errors not from a called function
- Cast ignored errors to void: `(void) unlink("/foo/bar")`
- Always check OOM; use `log_oom()` in program code
### Header Files
- Keep headers lean — prefer implementations in `.c` files
- Include the appropriate forward declaration header (`basic-forward.h`, `sd-forward.h`, `shared-forward.h`) instead of pulling in full headers
- Order: external headers (`<>`), then `sd-*` headers, then internal headers, alphabetically within each group
- No circular header dependencies
### Memory and Types
- Use `_cleanup_free_` and friends for automatic cleanup
- Use `alloca_safe()`, `strdupa_safe()` instead of raw `alloca()`, `strdupa()`
- Never use `off_t`; use `uint64_t` instead
- Use `unsigned` not `unsigned int`; `uint8_t` for bytes, not `char`
### Functions to Avoid
- `memset` → use `memzero()` or `zero()`
- `strcmp`/`strncmp` → use `streq()` / `strneq()`
- `strtol`/`atoi` → use `safe_atoli()`, `safe_atou32()`, etc.
- `basename`/`dirname` → use `path_extract_filename()` / `path_extract_directory()`
- `fgets` → use `read_line()`
- `exit()` → use `return` from main or `_exit()` in forked children
- `dup()` → use `fcntl(fd, F_DUPFD_CLOEXEC, 3)`
- `htonl`/`htons` → use `htobe32()` / `htobe16()`
### File Descriptors
- Always use `O_CLOEXEC` / `SOCK_CLOEXEC` / `MSG_CMSG_CLOEXEC` / ...
### Logging
- Library code (`src/basic/`, `src/shared/`): never log (except `LOG_DEBUG`)
- "Logging" functions log errors themselves; "non-logging" functions expect callers to log
- Non-fatal warnings: suffix with `, ignoring: %m"` or similar
### Integration Tests
- Never use `grep -q` in pipelines; use `grep >/dev/null` instead (avoids `SIGPIPE`)
## Pull Request Review Instructions
- Focus on coding style compliance from `docs/CODING_STYLE.md`
- Only leave comments for logic issues if you are very confident in your deduction
- Frame comments as questions
- Always consider you may be wrong
- Do not argue with contributors; assume they are right unless you are very confident
- Be extremely thorough — every single coding style violation should be reported
- Always check out the PR in a git worktree in `worktrees/`, review it locally and remove the worktree when finished.
## AI Contribution Disclosure