Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ Some tools also use a thin pointer file that simply routes them here — these a

| Tool | Pointer file (→ reads `AGENTS.md`) |
|------|------|
| Claude Code | [CLAUDE.md](CLAUDE.md) |
| Claude Code | [CLAUDE.md](CLAUDE.md), plus one per component guide |
| Gemini CLI | [GEMINI.md](GEMINI.md) |
| GitHub Copilot | [.github/copilot-instructions.md](.github/copilot-instructions.md) |

Claude Code reads `CLAUDE.md` and not `AGENTS.md`, so every directory holding a component guide also carries a `CLAUDE.md` whose only instruction is `@AGENTS.md`. Claude Code loads a nested `CLAUDE.md` on demand — when it first reads a file in that directory — so the component guide enters context automatically for whichever component is being worked on, without the root file pulling in all of them. Keep these pointers to the import alone; guidance belongs in `AGENTS.md`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Any other agent can read `AGENTS.md` directly — Cursor, for example, loads root and nested `AGENTS.md` files natively, so no Cursor-specific rule is needed. Personal AI-tool files (`.claude/`, `.cursor/`) are gitignored for local experimentation, except `.claude/settings.json` (committed team config).

If an `AGENTS.local.md` (repo root) or `~/AGENTS.local.md` (home) is present, read it at session start too — it holds machine-specific paths (gitignored). **Never put secrets there**; keep credentials in environment variables or a secret store.
Expand All @@ -21,7 +23,7 @@ If an `AGENTS.local.md` (repo root) or `~/AGENTS.local.md` (home) is present, re
**You are responsible for keeping this file accurate.** After completing work, check whether any of these apply:

- Added, removed, or renamed a top-level directory or component
- Added or removed a per-component `AGENTS.md`
- Added or removed a per-component `AGENTS.md` (add or remove its `CLAUDE.md` pointer to match)
- Changed the tech stack (new dependency in `go.mod`, new tool, removed technology)
- Changed build targets in `Makefile` / `Makefile.include`
- Changed global conventions (code style, error handling, testing patterns)
Expand Down Expand Up @@ -61,6 +63,8 @@ Each PMM component has a dedicated guide with architecture, directory structure,
| **API Tests** (integration tests) | [api-tests/AGENTS.md](api-tests/AGENTS.md) | `api-tests/**` |
| **Build & Packaging** | [build/AGENTS.md](build/AGENTS.md) | `build/**` |

A component guide covers only what is specific to its area. Global conventions — Go style, error handling, logging, testing, code generation — live in [Global Development Conventions](#global-development-conventions) and are deliberately **not** repeated in component guides: both files load together, so a restated rule costs context twice and creates a second place to forget to update it. When adding a rule, put it in the most specific guide — or guides, where a rule genuinely applies to more than one component but not to all — that covers it, and nowhere else. The one intentional exception is [PMM-specific choices](#pmm-specific-choices-agents-often-get-wrong), a short curated list of pitfalls that repeats a handful of rules on purpose.

---

## How AI agents should work in this repo
Expand Down Expand Up @@ -335,7 +339,7 @@ Core components and per-area guides: see [Component Guides](#component-guides) a
| **VictoriaMetrics** | Time-series metrics storage |
| **VMAlert** | Alerting rules evaluation |
| **Grafana** | Dashboards and visualization |
| **reform** | Go ORM for PostgreSQL (used in pmm-managed only — NOT gorm) |
| **reform** | Go ORM — NOT gorm. pmm-managed's PostgreSQL store, and pmm-agent's row mappers for monitored MySQL/PostgreSQL system views |
| **logrus** | Structured logging |
| **testify** | Test assertions (`assert`, `require` packages only — NOT suites) |
| **mockery** | Mock generation for Go interfaces |
Expand All @@ -354,21 +358,23 @@ Core components and per-area guides: see [Component Guides](#component-guides) a
- Use modern slice helpers (`slices.Contains`), range loops
- Use `sync.WaitGroup.Go` instead of `Add`/`go func`/`Done`, and don't copy a loop variable to use it in a closure (per-iteration scoping since Go 1.22)
- Don't use named return values
- Don't inline comments (`code // comment`); put comments on separate lines
- Don't inline comments (`code // comment`); put comments on separate lines — `//nolint` is the only exception
- Don't inline `err != nil` checks (`if err := f(); err != nil`); assign on one line, check on the next
- Don't add obvious/redundant comments; only comment non-obvious intent

### Error Handling
- Use `status.Error()` with proper gRPC codes for API errors
- Wrap errors with context: `fmt.Errorf("descriptive context: %w", err)`
- Return early on errors to avoid deep nesting
- Use `errors.Is()`, `errors.As()` or `errors.AsType()` for error inspection
- Use standard `errors` package, not `github.com/pkg/errors`
- Check `reform.ErrNoRows` for "not found" scenarios in pmm-managed
- Use standard `errors` package, not `github.com/pkg/errors` (existing uses may remain until refactored)
- Don't interpolate strings with `%q` in error messages; use `%s`, or `'%s'` when the value can contain spaces

### Logging
- Use `logrus` with structured fields
- Pass `*logrus.Entry` (not `*logrus.Logger`) to maintain context
- Format: `s.l.WithField("key", value).Error("message")`
- Don't interpolate strings with `%q` in log messages; use `%s`, or `'%s'` when the value can contain spaces
- Log to unbuffered stderr; let the process supervisor handle the rest

### Environment Variables
Expand All @@ -387,7 +393,6 @@ Core components and per-area guides: see [Component Guides](#component-guides) a

### Code Generation
- Protobuf/gRPC: `make gen` from repo root
- reform ORM: `//go:generate go tool reform` (pmm-managed only)
- Mocks: `mockery` per `.mockery.yaml`
- **Never edit generated files** (`.pb.go`, `.pb.gw.go`, `*_reform.go`, `*.pb.validate.go`, swagger specs, `json/client/`)

Expand Down
1 change: 1 addition & 0 deletions admin/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
5 changes: 3 additions & 2 deletions agent/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ pmm-agent has **no direct database access**. All state comes from pmm-managed vi
- Use table-driven tests with golden files for parsers
- Use `utils/templates` to render exporter args from server-provided templates
- Follow the supervisor pattern — let the supervisor manage all agent lifecycle
- Check `reform.ErrNoRows` explicitly for "not found" when the QAN collectors read monitored-database system views

### Don't
- Don't hardcode exporter binary paths — use `config.Paths`
- Don't bypass the supervisor for agent lifecycle management
- Don't use raw SQL — the agent has no database; all data comes via gRPC
- Don't persist agent state locally — pmm-agent has no datastore of its own; configuration arrives from pmm-managed over gRPC. Querying *monitored* databases is a separate matter: the QAN collectors read their system views through reform
- Don't modify exporter args directly — they come from server templates

## Testing
Expand All @@ -96,7 +97,7 @@ pmm-agent has **no direct database access**. All state comes from pmm-managed vi

## Code Generation

- **reform**: not used (agent has no DB)
- **reform**: used by the QAN collectors to map monitored-database system views — `agents/mysql/perfschema`, `agents/postgres/pgstatstatements`, `agents/postgres/pgstatmonitor`. Each has a `//go:generate go tool reform` directive producing `models_reform.go`
- **mockery**: generates mocks for supervisor, connectionChecker, serviceInfoBroker interfaces
- **protobuf**: agent consumes types from `/api`; run `make gen` from repo root if proto files change

Expand Down
1 change: 1 addition & 0 deletions agent/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 0 additions & 1 deletion api-tests/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ docker run --network host -e PMM_SERVER_URL=http://admin:admin@127.0.0.1 pmm-api
### Do
- Make tests **idempotent** — tests must clean up after themselves
- Use helper functions in `helpers.go` for common setup (creating nodes, services, agents)
- Use `testify/assert` and `testify/require` for assertions
- Test both success and error paths (invalid input, not found, permission denied)
- Use the generated Swagger clients for API calls (same clients as pmm-admin)
- Test with the `t.Cleanup()` pattern to ensure resources are removed even on failure
Expand Down
1 change: 1 addition & 0 deletions api-tests/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 4 additions & 4 deletions api/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,19 @@ Nothing enforces this: `buf lint` checks proto identifiers, not the path strings

## Patterns and Conventions

Go style, error handling, and the "never edit generated files" rule follow the [global conventions](../AGENTS.md#global-development-conventions) and are not repeated here. What follows is specific to the API definitions.

### Do
- Edit only `.proto` files — they are the source of truth
- Run `make gen` (from repo root) after any proto change
- Use `go tool buf lint` to validate proto files before committing
- Add `(validate.rules)` annotations for field validation
- Use `google.api.http` annotations for REST endpoint mapping
- Use gRPC status codes (`codes.NotFound`, `codes.InvalidArgument`, etc.) not HTTP status codes
- Follow RESTful conventions for HTTP mappings (GET for reads, POST for creates, PUT for updates, DELETE for deletes)
- Follow RESTful conventions for HTTP mappings — GET for reads, POST for creates, PUT for updates (partial ones included), DELETE for deletes
- Name REST paths per [REST Path Naming](#rest-path-naming) above
- Add comments to proto messages and fields — they become API documentation

### Don't
- **Never edit generated files** (`*.pb.go`, `*.pb.gw.go`, `*.pb.validate.go`, `*.swagger.json`, `json/client/`)
- Don't use `patch:` in a `google.api.http` annotation — every update is `put:`, partial ones included: `ChangeAgent`, `ChangeService` and `ChangeSettings` each send only the fields being changed, and all three are PUT. There is no `patch:` anywhere in `api/`; keep it that way. `buf lint` won't flag one — its `STANDARD` rules check proto identifiers, not the method keys inside the annotation
- Don't introduce breaking changes to `v1` APIs (use `v1beta1` for experimental APIs)
- Don't add business logic to the API layer — it belongs in `managed/services/`
- Don't skip validation annotations on incoming request messages
Expand Down
1 change: 1 addition & 0 deletions api/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions build/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions dashboards/dashboards/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions dashboards/pmm-app/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
42 changes: 5 additions & 37 deletions managed/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ type Node struct {
```

**Key conventions:**
- Models: `managed/models/*_model.go` with `//go:generate` directives
- Models: `managed/models/*_model.go` — a new model file must carry its own `//go:generate go tool reform` line, or `make gen` silently generates no `*_reform.go` for it
- Generated: `managed/models/*_reform.go` (never edit)
- CRUD helpers: `managed/models/*_helpers.go`
- Always accept `reform.Querier` parameter (works with both `*reform.DB` and `*reform.TX`)
Expand Down Expand Up @@ -128,60 +128,28 @@ PMM supports HA via **Raft consensus** (`services/ha/`):

## Patterns and Conventions

Go style, error handling, logging, and code generation follow the [global conventions](../AGENTS.md#global-development-conventions) and are not repeated here. What follows is specific to pmm-managed.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Do
- Prefer modern Go idioms (context, error wrapping with `%w`)
- Use modern slice helpers (`slices.Contains`), range loops
- Use `any` instead of `interface{}`
- Define small interfaces in `deps.go` files for dependency injection and mocking
- Use `status.Error()` with proper gRPC codes for API errors
- Check `reform.ErrNoRows` for "not found" scenarios
- Wrap errors: `fmt.Errorf("descriptive context: %w", err)`
- Return early on errors to avoid deep nesting
- Use `errors.Is()` / `errors.As()` for error type checking
- For new or updated code, prefer the standard `errors` package over `github.com/pkg/errors` (existing uses may remain until refactored)
- Use structured logging: `s.l.WithField("key", value).Error("message")`
- Pass `*logrus.Entry` (not `*logrus.Logger`)
- Use RESTful conventions in proto HTTP annotations

### Don't
- Don't connect to a real database in unit tests — use `github.com/DATA-DOG/go-sqlmock` to mock SQL queries; reserve `testdb.Open` for integration tests that genuinely require fixtures or migrations
- Don't use `gorm` or other ORMs — only `reform`
- Don't edit generated files (`*_reform.go`, `*.pb.go`, `*.pb.gw.go`, swagger specs)
- Don't skip `make gen` after proto/model changes
- Don't comment on every line — only where clarity is needed
- Don't inline comments (`code // comment`) — put comments on separate lines; `//nolint` is the only exception
- Don't inline `err != nil` checks (`if err := f(); err != nil`) — assign on one line, check on the next
- Don't use named return values in functions
- Don't commit test binaries or artifacts
- Don't create subshells in Makefiles without reason

## Testing

### Unit Tests
- Use `testify/assert` and `testify/require`
- Mock generation via `mockery` (config in `.mockery.yaml`)
- Interface-based deps in `deps.go` files enable mocking
- `mock_*_test.go` files generated by mockery
- Mock DB with `go-sqlmock` (wraps a `reform.DB`) for unit tests; use `testdb.Open` only when fixtures or migrations are required
- `mock_*_test.go` files are generated by mockery
- `go-sqlmock` wraps a `reform.DB`, so mocked queries are matched as SQL text
- Run: `make test` (in managed/) or `make test-common` (from root)

### Integration Tests
- Located in `/api-tests/` (separate directory)
- Run against live PMM Server: `make api-test`

### Test Data
- `testdata/pg/` — PostgreSQL fixtures
- `testdata/victoriametrics/` — VictoriaMetrics configs

## Code Generation

1. **Protocol Buffers** — `make gen` from repo root
2. **reform** — `//go:generate go tool reform` on model files
3. **mockery** — mock generation per `.mockery.yaml`
4. **swagger** — API docs from proto annotations

Always run `make gen` after modifying `.proto` files, reform models, or interface signatures.

## Key Files to Reference

- `managed/cmd/pmm-managed/main.go` — application bootstrap, all service wiring
Expand Down
1 change: 1 addition & 0 deletions managed/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions qan-api2/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions ui/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
1 change: 1 addition & 0 deletions vmproxy/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
Loading