Files
zoneminder/AGENTS.md
Isaac Connor 6c4890992d fix: upgrade ESLint config to v9 flat config format
Complete the migration from .eslintrc.js to eslint.config.js flat
config format for ESLint 9 compatibility. Add valid-jsdoc: off
(removed in ESLint 9 but enabled by eslint-config-google) and the
missing operator-linebreak: off override. Update the HLS ignore path
to match current version. Update CI workflow to install ESLint 9 and
its flat config dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:25:54 -05:00

336 lines
13 KiB
Markdown

# AI Agent Development Guide for ZoneMinder
> **Note**: This file guides AI coding agents (Claude Code, GitHub Copilot, Cursor, etc.) working on ZoneMinder.
> CLAUDE.md is a symlink to this file.
## Quick Reference (MANDATORY RULES)
1. **Testing First**: Write tests BEFORE/DURING implementation - NEVER skip. Tests written "later" never get written.
2. **Build System**: CMake with C++17, out-of-source builds in `build/`. In-source builds pollute the repo.
3. **Feature Workflow**: GitHub Issue -> Feature Branch -> Implement FULLY -> Tests Pass -> Get Approval -> Merge to master. Feature branches keep master stable.
4. **Commits**: Conventional format (`feat:`/`fix:`/`test:`), reference issues (`refs #n` or `fixes #n`). Enables automated changelog.
5. **Pre-Commit**: Tests pass, build succeeds, linting clean, no warnings.
6. **Never merge without user approval. Never leave features half-implemented.**
---
## What is ZoneMinder?
Linux-based CCTV surveillance system: capture, analysis, recording, and monitoring of video cameras.
- **C++ daemons** (`src/`) - Capture (`zmc`), analysis (`zma`), streaming (`zms`), utility (`zmu`)
- **PHP web interface** (`web/`) - Bootstrap + jQuery UI, AJAX endpoints in `web/ajax/`, views in `web/views/`
- **REST API** (`web/api/`) - CakePHP 2.x, controllers in `web/api/app/Controller/`, JWT auth
- **Perl scripts** (`scripts/`) - Daemon control (`zmdc.pl`), migrations (`zmupdate.pl`), filtering (`zmfilter.pl`)
- **MySQL database** (`db/`) - Schema in `db/zm_create.sql.in`, migrations in `db/zm_update-*.sql` (60+ versions)
---
## Architecture
### Data Flow
```
Camera -> zmc (capture) -> Shared Memory -> zma (analysis) -> Event Recording
| |
zms (streaming) Database + Disk
| |
Web Browsers <- Web Interface/API <- MySQL Storage
```
### Key Patterns
- **Shared Memory**: `zmc` writes frames to `/dev/shm`; `zma` and `zms` read from same buffers. Zero-copy for performance — this is why ZM can handle many cameras on modest hardware.
- **Monitor-Centric**: `Monitor` class (`src/zm_monitor.cpp/h`) is the central orchestrator. One monitor = one DB row = one set of daemons. Most changes to camera handling flow through this class.
- **Pluggable Cameras**: Abstract `Camera` base with: `LocalCamera` (V4L2), `RemoteCameraRTSP`, `RemoteCameraHTTP`, `FFmpegCamera`, `LibVLCCamera`, `LibVNCCamera`. Add new camera types by subclassing Camera.
- **Event-Driven Recording**: Motion detection triggers `Event` objects with pre/post alarm buffers. Lifecycle: Create -> Record -> Close -> Archive/Delete. Events are the core unit of recorded footage.
- **Multi-Server Clustering**: Database-coordinated distributed architecture with shared monitors and storage.
### Directory Structure (Key Paths)
```
src/ C++ core (86+ source files): zm_monitor.*, zm_camera.*, zm_event.*, zm_zone.*, zm_image.*, zm_ffmpeg*.*
web/ PHP web interface
ajax/ AJAX handlers
includes/ PHP libraries and functions
views/ UI templates
skins/ Themes (classic skin)
js/, css/ Frontend assets
api/app/Controller/ CakePHP REST API controllers
api/app/Model/ CakePHP REST API models
scripts/ Perl system management (.in templates)
db/ Schema (zm_create.sql.in) and migrations (zm_update-*.sql)
tests/ Catch2 unit tests + test data
misc/ Server configs (apache, nginx, systemd)
dep/ Vendored deps (catch2, jwt-cpp)
.github/workflows/ CI/CD pipelines
```
---
## Development Workflow (MANDATORY)
### For every feature or bug fix:
1. **Create GitHub Issue**: `gh issue create --title "..." --label enhancement|bug`
2. **Create Feature Branch**: `git checkout -b <issue>-<description>` from master
3. **Write failing test first** (TDD)
4. **Implement** - follow existing patterns, keep changes minimal
5. **Build & test** - must all pass (see Build and Testing sections)
6. **Commit** in logical chunks with conventional messages
7. **Request user approval** - never merge without it
8. **After approval**: merge to master, delete branch, push, verify issue closes
### Technology-Specific Notes
- **C++ changes** (`src/`): Rebuild required, run ctest. Daemons must be restarted to pick up changes.
- **PHP/JS changes** (`web/`): No rebuild needed — PHP is interpreted, changes are live immediately. Test in browser + ESLint.
- **Database schema** (`db/`): Create migration file `zm_update-X.X.X.sql` for existing installs, also update `db/zm_create.sql.in` for fresh installs. Both must result in the same schema.
- **Perl scripts** (`scripts/`): Edit `.in` template files (NOT generated scripts). cmake substitutes `@ZM_*@` variables to produce the final scripts. When testing perl changes, any perl script will try to read /etc/zm/zm.conf and needs to be run as a user with permission to read it. Best to run with sudo -u www-data
- **API changes** (`web/api/`): Clear CakePHP cache (`tmp/cache/`) if you change models or routes. Test endpoints with curl.
### Example Workflow
```bash
gh issue create --title "Add event favorites" --label enhancement
# Note issue number, e.g. #42
git checkout master && git pull && git checkout -b 42-event-favorites
# Write test first, implement, build & test
cd build && cmake --build . && ctest
npx eslint --ext .js.php,.js .
# Commit
git add <files>
git commit -m "feat: add favorites toggle to event view refs #42"
# Ask user for approval, then after approval:
git checkout master && git merge 42-event-favorites
git push origin master
git branch -d 42-event-favorites && git push origin --delete 42-event-favorites
```
---
## Build System
```bash
# Standard build (out-of-source required)
mkdir build && cd build
cmake ..
cmake --build .
# Debug build with tests
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST_SUITE=ON ..
cmake --build .
# Build specific target
cmake --build . --target zmc
```
### Key CMake Options
| Option | Values | Default | Notes |
|--------|--------|---------|-------|
| `CMAKE_BUILD_TYPE` | Release/Debug/Optimised | Release | |
| `BUILD_TEST_SUITE` | ON/OFF | OFF | Enables Catch2 tests |
| `ENABLE_WERROR` | ON/OFF | OFF | Warnings as errors (CI uses ON) |
| `ASAN` | ON/OFF | OFF | AddressSanitizer |
| `TSAN` | ON/OFF | OFF | ThreadSanitizer (mutually exclusive with ASAN) |
| `ZM_CRYPTO_BACKEND` | openssl/gnutls | | |
| `ZM_JWT_BACKEND` | libjwt/jwt_cpp | | |
| `ZM_TARGET_DISTRO` | FreeBSD/fc/el/OS13 | (Debian) | Platform-specific paths |
| `ZM_ONVIF` | ON/OFF | ON | ONVIF camera support |
---
## Testing Requirements (MANDATORY)
### Workflow
1. Write a failing test that reproduces the issue or validates the feature
2. Implement the fix/feature
3. Run tests - verify they PASS
4. Run full test suite for regressions
5. Only then commit
### C++ Unit Tests (Catch2)
```bash
# Build with tests
cmake -DBUILD_TEST_SUITE=ON .. && cmake --build .
# Run all tests
ctest
# Or directly with more output
./tests/tests
# Run specific test / list tests
./tests/tests "[Box]"
./tests/tests --list-tests
./tests/tests "~[notCI]"
```
**Test location**: `tests/` directory
**Existing modules**: `zm_box.cpp`, `zm_comms.cpp`, `zm_crypt.cpp`, `zm_font.cpp`, `zm_poly.cpp`, `zm_utils.cpp`, `zm_vector2.cpp`
**Framework**: Catch2 with custom header `tests/zm_catch2.h`, main in `tests/main.cpp`
**Test data**: `tests/data/fonts/`
### JavaScript Linting
```bash
npx eslint --ext .js.php,.js . # Lint all
npx eslint --fix web/js/ # Auto-fix
```
ESLint config: `eslint.config.js` (ESLint 9 flat config, Google style guide). Runs in CI via `.github/workflows/ci-eslint.yml`.
### PHP/API Testing
Manual testing required. Test endpoints with curl/browser. Verify JSON responses, auth (session + JWT), error handling, DB persistence.
### Database Migration Testing
```bash
sudo zmupdate.pl --check # Check for updates
sudo zmupdate.pl # Apply migrations
```
When creating migrations: test upgrade path AND verify fresh install matches migrated schema.
### CI Notes
- Unit tests are **DISABLED** in CI (`BUILD_TEST_SUITE=0`) — some tests need hardware/network access. Running locally goes beyond CI requirements but is still expected for local development.
- CI does: multi-platform builds (Debian/Ubuntu/CentOS with GnuTLS/OpenSSL + libjwt/jwt_cpp matrix), ESLint, CodeQL security scanning
---
## Code Standards
### C++ (`src/`)
- **C++17** standard, follow existing patterns in file/module
- **Memory**: RAII, smart pointers where appropriate
- **Error handling**: Exceptions for exceptional cases, return codes for expected errors
- **Logging**: Use printf-style `Debug(1, "msg %s", val)`, `Info(...)`, `Warning(...)`, `Error(...)`, `Fatal(...)` — NOT iostream style. The number in Debug() is verbosity level (1-9).
### PHP (`web/`, `web/api/`)
- PSR-12 where practical, follow existing conventions
- **Security**: Validate ALL user input, use prepared statements (NOT string interpolation — legacy code has SQL injection bugs we're fixing), sanitize output with htmlspecialchars/json_encode, CSRF protection on forms
- CakePHP 2.x framework in `web/api/` — we're stuck on 2.x due to migration cost, don't try to upgrade it
### JavaScript (`web/js/`, inline in PHP)
- Google JavaScript Style Guide (ESLint configured)
- jQuery + Bootstrap (legacy codebase), ES6+ where browser support allows
### Perl (`scripts/`)
- Edit `.in` template files, NOT generated scripts — cmake substitutes variables like `@ZM_LOGDIR@` to produce the final scripts
- Use `ZoneMinder::` modules, `ZoneMinder::Logger` for logging
- Rerun cmake after editing `.in` files to regenerate the actual scripts
---
## Commit Standards
**Conventional format** with issue references:
```
feat|fix|test|docs|chore|refactor|perf|style: description refs #N
```
- `refs #N` references issue; `fixes #N` closes it
- Imperative mood ("add feature" not "added feature")
- Be detailed and descriptive, no vague summaries
- One logical change per commit
- **No superlative language** ("comprehensive", "critical", "major", "massive") — AI agents tend to use these; they make commit logs unreadable
- Split unrelated changes into separate commits — makes bisecting and reverting possible
---
## Pre-Commit Checklist (Single Source of Truth)
Before committing or claiming complete:
- [ ] Tests written/updated BEFORE or DURING implementation
- [ ] Build succeeds: `cmake --build .` (C++ changes)
- [ ] C++ tests pass: `ctest` or `./tests/tests`
- [ ] JavaScript linting passes: `npx eslint --ext .js.php,.js .`
- [ ] Manual testing done (PHP/web changes)
- [ ] No compiler warnings or linter errors
- [ ] Code follows existing patterns
- [ ] Commit messages follow conventional format with issue reference
- [ ] Feature is COMPLETE (not half-implemented)
- [ ] State which tests were run and their results
**NEVER commit if**: tests failing, tests missing for new code, build fails, warnings exist, feature incomplete.
---
## Debugging Quick Reference
```bash
# Debug build
cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build .
# AddressSanitizer
cmake -DCMAKE_BUILD_TYPE=Debug -DASAN=ON .. && cmake --build .
# Debug logging
export ZM_DBG_LEVEL=9 ZM_DBG_LOG=/tmp/debug.log
./src/zmc -m 1
# Log locations: /var/log/zm/ (zmc_m1.log, zma_m2.log, etc.)
# Runtime log level: kill -USR1 <pid> (increase) / kill -USR2 <pid> (decrease)
```
---
## Contributing (For Humans)
- **Bug reports & features**: [GitHub Issues](https://github.com/ZoneMinder/zoneminder/issues) (read [posting rules](https://github.com/ZoneMinder/ZoneMinder/wiki/Github-Posting-Rules) first)
- **Support**: [Forums](https://forums.zoneminder.com), [Slack](https://zoneminder-chat.slack.com), [Discord](https://discord.gg/tHYyP9k66q)
- **PRs**: Fork -> feature branch -> implement + test -> PR. Maintainers merge after review.
- **Docs**: https://zoneminder.readthedocs.org (source in `docs/`)
---
## grepai - Semantic Code Search
**Use grepai as PRIMARY tool for code exploration and search.**
### When to Use grepai (REQUIRED)
Use `grepai search` INSTEAD OF Grep/Glob for:
- Understanding what code does or where functionality lives
- Finding implementations by intent ("authentication logic", "error handling")
- Exploring unfamiliar parts of the codebase
Only use Grep/Glob for exact text matching (variable names, imports, specific strings) or file path patterns.
If grepai fails (not running, index unavailable), fall back to Grep/Glob.
### Usage
```bash
grepai search "user authentication flow" --json --compact
grepai search "JWT token validation" --json --compact
```
### Call Graph Tracing
```bash
grepai trace callers "HandleRequest" --json
grepai trace callees "ProcessOrder" --json
grepai trace graph "ValidateToken" --depth 3 --json
```
### Workflow
1. `grepai search` to find relevant code
2. `grepai trace` to understand function relationships
3. `Read` tool to examine files from results
4. Grep only for exact string searches if needed