|
| 1 | +# AGENTS.md — Shared C++ Engineering Baseline |
| 2 | + |
| 3 | +## Scope |
| 4 | + |
| 5 | +These rules apply to LiveKit C++ projects that consume `cpp-tools`. If a consuming |
| 6 | +repository has an `AGENTS.md` with conflicting rules they should take priority. |
| 7 | + |
| 8 | +## Safety and Determinism |
| 9 | + |
| 10 | +- Design for predictable memory use, execution time, and failure behavior. |
| 11 | +- Avoid heap allocation when practical. In real-time, callback, and steady-state |
| 12 | + paths, allocate resources during initialization and reuse them. |
| 13 | +- Prefer stack storage, RAII, fixed-capacity storage, and bounded pools or |
| 14 | + queues. Document unavoidable dynamic allocation in time-sensitive code. |
| 15 | +- Put explicit bounds on queues, retries, buffers, worker counts, and waits. |
| 16 | + Define observable behavior for exhaustion and overload. |
| 17 | +- Do not block real-time or callback threads with file/network I/O, sleeps, |
| 18 | + unbounded work, allocation, or contended locks. Offload work through bounded |
| 19 | + mechanisms with clear back-pressure. |
| 20 | +- Avoid unbounded recursion and large stack objects. Account for stack limits on |
| 21 | + embedded targets. |
| 22 | + |
| 23 | +## Errors and Failure Modes |
| 24 | + |
| 25 | +- Use return values for expected failures instead of exceptions: |
| 26 | + - `std::optional<T>` when absence is expected and needs no diagnostic. |
| 27 | + - `bool` for a simple success/failure result. |
| 28 | + - `Result<T, E>`, `expected` (C++23 or higher), or equivalent when callers need a typed error. |
| 29 | +- Callers must inspect status-bearing return values. Mark important results |
| 30 | + `[[nodiscard]]` where practical. |
| 31 | +- Do not throw through C, FFI, callback, destructor, real-time, or |
| 32 | + resource-constrained boundaries. |
| 33 | +- Reserve exceptions for genuinely exceptional failures when the consuming |
| 34 | + project permits them. Catch them at a well-defined boundary and convert them |
| 35 | + to the project's error model. |
| 36 | +- Validate external inputs and cross-boundary data. Keep state valid on failure |
| 37 | + and prefer fail-safe behavior over partial updates. |
| 38 | + |
| 39 | +## Memory, Ownership, and Lifetime |
| 40 | + |
| 41 | +- Make ownership explicit. Prefer values and RAII types; do not use raw owning |
| 42 | + pointers. |
| 43 | +- Use `std::unique_ptr` for exclusive dynamic ownership and `std::shared_ptr` |
| 44 | + only when ownership is genuinely shared. |
| 45 | +- Avoid heap allocations as much as possible. If code is heap allocating in a loop |
| 46 | + or a high-frequency path, second guess the design and consider alternatives. |
| 47 | +- Keep object lifetimes and teardown order deterministic. Destructors must not |
| 48 | + throw. |
| 49 | +- Avoid hidden copies of large buffers. Make copy and move behavior intentional, |
| 50 | + especially for media, sensor, and message data. |
| 51 | +- Declare data at the smallest useful scope and initialize it before use. |
| 52 | + |
| 53 | +## Types, Arithmetic, and Units |
| 54 | + |
| 55 | +- Prefer STL types over third-party dependencies when possible. |
| 56 | +- Prefer fixed-width integers from `<cstdint>` when width or signedness matters, |
| 57 | + including serialization, FFI, hardware, timestamps, IDs, and public APIs. |
| 58 | +- Use platform-sized primitive integers only when the value is intentionally |
| 59 | + platform-sized or compatibility requires it. |
| 60 | +- Avoid implicit narrowing and mixed signed/unsigned arithmetic. Validate ranges |
| 61 | + before conversions and arithmetic that can overflow. |
| 62 | +- Represent durations and time points with `std::chrono`; use a monotonic clock |
| 63 | + for elapsed time, deadlines, and timeouts. |
| 64 | +- Make physical units explicit with strong or clearly named types, such as `_us` |
| 65 | + for microseconds. Do not pass ambiguous raw numeric values across interfaces. |
| 66 | + |
| 67 | +## Concurrency |
| 68 | + |
| 69 | +- Document which threads call an API and whether each type is thread-safe. |
| 70 | +- Minimize shared mutable state. Protect it with clear synchronization and keep |
| 71 | + critical sections short. |
| 72 | +- Never call user code while holding an internal lock. |
| 73 | +- Use bounded waits and define cancellation and shutdown behavior. Join worker |
| 74 | + threads outside locks. |
| 75 | +- Treat atomics and lock-free code as specialized tools; document memory-order |
| 76 | + reasoning and test concurrency paths under stress. |
| 77 | + |
| 78 | +## Design and Readability |
| 79 | + |
| 80 | +- Keep functions focused and short, roughly 60 lines or fewer when practical. |
| 81 | +- Prefer straightforward control flow over clever abstractions. Document any |
| 82 | + deliberate tradeoff between readability, determinism, and performance. |
| 83 | +- Use `enum class`, `nullptr`, explicit conversions, and const-correct |
| 84 | + interfaces. |
| 85 | +- Check non-void return values and make ignored results explicit. |
| 86 | +- Use `git mv` when moving or renaming tracked files. |
| 87 | + |
| 88 | +## Portability |
| 89 | + |
| 90 | +- Avoid undefined behavior, compiler-specific assumptions, and dependence on |
| 91 | + host endianness, alignment, or primitive widths. |
| 92 | +- Keep cross-platform and cross-architecture boundaries explicit. Test all |
| 93 | + supported targets defined by the consuming repository. |
| 94 | +- Keep third-party implementation details out of public headers and ABI |
| 95 | + boundaries. |
| 96 | + |
| 97 | +## Style |
| 98 | + |
| 99 | +- Add the LiveKit copyright header with the correct year to new code files. |
| 100 | +- Prefer the constructor initializer list rather than variable declaration |
| 101 | + and assignment in the constructor body. |
| 102 | +- For Doxygen/doc comments, prefer `///` comment style and use @brief, |
| 103 | + @param, @return, @throw, @ref, @note, @warning as applicable. |
| 104 | + |
| 105 | +## Project-Owned clangd Configuration |
| 106 | + |
| 107 | +- Each consuming project must provide its own `.clangd`; compilation database |
| 108 | + locations and flags are project-specific and are not shared by `cpp-tools`. |
| 109 | +- Verify `.clangd` points clangd at the project's generated compilation database |
| 110 | + before relying on IDE diagnostics. |
| 111 | +- clang-tidy does not read `.clangd`. Before running clang-tidy, generate a valid |
| 112 | + `compile_commands.json` and pass its build directory to `clang-tidy.sh`. |
| 113 | + |
| 114 | +## Verification |
| 115 | + |
| 116 | +- Adhere to the shared `.clang-format` and `.clang-tidy` configurations. |
| 117 | +- After C++ changes, run `./cpp-tools/clang-format.sh` with the consuming |
| 118 | + project's paths. Use `--fix` when needed, then rerun the check. |
| 119 | +- Generate the consuming project's compilation database and run |
| 120 | + `./cpp-tools/clang-tidy.sh` with its documented build directory and filters. |
| 121 | +- Do not bypass formatter or static-analysis failures. Keep suppressions narrow, |
| 122 | + local, and justified in code. |
| 123 | +- Add deterministic tests for normal, boundary, overload, timeout, cancellation, |
| 124 | + and failure behavior. Avoid timing-only sleeps when a condition or simulated |
| 125 | + clock can be used. |
| 126 | +- Benchmark or stress-test new time-sensitive or resource-sensitive behavior and |
| 127 | + verify that configured limits are enforced. |
0 commit comments