From fd541bca2cfd7755f036f9f8b824a5408fec5ccd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 13 Oct 2026 02:20:32 -1000 Subject: [PATCH] [core] Document constexpr constants, per-model schemas and isolated build tests in AGENTS.md (#20617) --- AGENTS.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 5031c4a457..e00888b5f7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -144,6 +144,9 @@ file does, and it is the authority when they disagree. The most useful starting * **C++ Preprocessor Directives:** * **Avoid `#define` for constants:** Using `#define` for constants is discouraged and should be replaced with `const` variables or enums. + * **A constant with a compile-time initializer is `static constexpr`,** not `static const`, at namespace scope + and inside functions; `static const` is only for a value computed at run time. A function-local + `static constexpr` table is a static constant to clang-tidy, so it is `UPPER_SNAKE_CASE` too. * **Use `#define` only for:** - Conditional compilation (`#ifdef`, `#ifndef`) - Compile-time sizes calculated during Python code generation (e.g., configuring `std::array` or `StaticVector` dimensions via `cg.add_define()`) @@ -217,6 +220,9 @@ file does, and it is the authority when they disagree. The most useful starting listener base (a second vptr per entity). * **A global feature define** says the code is compiled in, not that this instance uses it: keep the per-instance check (`if (this->aux_device_ == ...)`) inside the `#ifdef`. + * **Model-specific code** sits behind a define the Python side emits only for configs that use that model + (`cg.add_define("USE_SELEC_METER_EM4M")`), so the other models' builds leave it out; the runtime model check + stays inside the `#ifdef`. * **Failures after `setup()`:** `mark_failed()` is for `setup()` only. A runtime read or write failure sets `status_set_warning()` and the next success clears it; writes skip work when `is_failed()`. * **Fixed platform facts compile to nothing:** gate on `#ifdef USE_ESP8266` and friends, not on a @@ -567,6 +573,9 @@ file does, and it is the authority when they disagree. The most useful starting An intentionally empty body (an unused `register_dumper` builder, for example) gets a one-line docstring, not `pass`. * **Configuration Validation:** + * **One schema per model:** when a component's options depend on a `model` key, use `cv.typed_schema({...}, + key=CONF_MODEL, default_type=...)` with each model's own keys and defaults, not one schema plus a validator + that rejects keys and fills defaults afterwards. * **Reuse existing validators:** Before writing a custom validator, check for an existing one in `config_validation.py` and compose it in `cv.All(...)` rather than duplicating logic across components. For example, rename a config key with `cv.rename_key(CONF_OLD, CONF_NEW, removed_in="2026.6.0")`, and reject mutually-exclusive keys with `cv.has_at_most_one_key(...)` / `cv.has_exactly_one_key(...)`. See how `api` composes `cv.has_exactly_one_key` + `cv.rename_key`. * **Common Validators:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`. * **Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`. @@ -609,6 +618,9 @@ file does, and it is the authority when they disagree. The most useful starting `on_tx` lambda with a static counter and `inject_to_rx_buffer` (see `uart_mock_ld2412_requery_after_giveup.yaml`). * **Keep build-test variants few:** one `test..yaml` per platform the code actually differs on. A variant per board for platform-identical code only adds CI time. + * **A test that needs a shared component in a particular state** (`esp32_ble` security settings, an option + that PSRAM rejects) cannot be grouped: the merged block wins and the whole batch fails. Add the component to + `ISOLATED_COMPONENTS` in `script/analyze_component_buses.py`, or move the option to a `test-` file. * **Component Tests:** YAML-based compilation tests are located in `tests/`. The structure is as follows: ``` tests/