{"owner":"wled","repo":"WLED","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# AGENTS.md — WLED AI Coding Agent & AI Code Review Reference\n\nWLED is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable LEDs,\nwith a web UI (HTML/JS/CSS). Built with PlatformIO (Arduino framework) and Node.js tooling.\n\nSee also: `.github/copilot-instructions.md`, `.github/agent-build.instructions.md`,\n`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`,\n`docs/hardening.instructions.md`, `docs/securecode.instructions.md`.\n\nAlways reference these instructions - including coding guidelines in `docs/` - first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.\n\n## Build Commands\n\n| Command | Purpose | Timeout |\n|---|---|---|\n| `npm ci` | Install Node.js deps (required first) | 30s |\n| `npm run build` | Build web UI into `wled00/html_*.h` / `wled00/js_*.h` | 30s |\n| `npm test` | Run test suite (Node.js built-in `node --test`) | 2 min |\n| `npm run dev` | Watch mode — auto-rebuilds web UI on changes | continuous |\n| `pio run -e esp32dev` | Build firmware (ESP32, most common target) | 5 min |\n| `pio run -e nodemcuv2` | Build firmware (ESP8266) | 5 min |\n\n**Always run `npm ci && npm run build` before `pio run`.** The web UI build generates\nrequired C headers for firmware compilation.\n\n### Running a Single Test\n\nTests use Node.js built-in test runner (`node:test`). The single test file is\n`tools/cdata-test.js`. Run it with:\n\n```bash\nnpm test                   # runs all tests via `node --test`\nnode --test tools/cdata-test.js  # run just that file directly\n```\n\nThere are no C++ unit tests. Firmware is validated by successful compilation across\ntarget environments. Always build after code changes: `pio run -e esp32dev`.\n\n### Common Firmware Environments\n\n`esp32dev`, `nodemcuv2`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi`, `lolin_s2_mini`\n\n### Recovery / Troubleshooting\n\n```bash\nnpm run build -- -f              # force web UI rebuild\nrm -f wled00/html_*.h wled00/js_*.h && npm run build  # clean + rebuild UI\npio run --target clean           # clean PlatformIO build artifacts\nrm -rf node_modules && npm ci    # reinstall Node.js deps\n```\n\n## Project Structure\n\n```text\nwled00/              # Main firmware source (C++)\n  data/              # Web UI source (HTML/JS/CSS) — tabs for indentation\n  html_*.h, js_*.h   # Auto-generated (NEVER edit or commit)\n  src/               # Sub-modules: fonts, bundled dependencies (ArduinoJSON)\nusermods/            # Community usermods (each has library.json + .cpp/.h)\nplatformio.ini       # Build configuration and environments\npio-scripts/         # PlatformIO build scripts (Python)\ntools/               # Node.js build tools (cdata.js) and tests\ndocs/                # Coding convention docs\n.github/workflows/   # CI/CD (GitHub Actions)\n```\n\n### Branch / Release Structure\n\n```text\nmain                # Main development trunk (daily/nightly) 17.0.0-dev. Target branch for PRs.\n  ├── V5            # special branch: code rework for esp-idf 5.5.x and new MCU types: esp32-c5, esp32-c6, esp32-p4 (unstable)\n16_x                # maintenance for release 16.0.x\n0_15_x              # maintenance (bugfixes only) for previous release 0.15.x\n(tag) v0.14.4       # old version 0.14.4 (no maintenance)\n(tag) v0.13.3       # old version 0.13.3 (no maintenance)\n(tag) v0. ... . ... # historical versions 0.12.x and before\n```\n\n## C++ Code Style (wled00/, usermods/)\n\n### Formatting\n- **2-space indentation** (no tabs in C++ files)\n- K&R brace style preferred (opening brace on same line)\n- Single-statement `if` bodies may omit braces: `if (a == b) doStuff(a);`\n- Space after keywords (`if (...)`, `for (...)`), no space before function parens (`doStuff(a)`)\n- No enforced line-length limit\n\n### Comments\n- `//` for inline (always space after), `/* */` for block comments\n- Important: AI-generated source code blocks **must be mark with `// AI: below section was generated by an AI` / `// AI: end`**\n\n### Naming Conventions\n| Kind | Convention | Examples |\n|---|---|---|\n| Functions, variables | camelCase | `setRandomColor()`, `effectCurrent` |\n| Classes, structs | PascalCase | `BusConfig`, `UsermodTemperature` |\n| Macros, constants | UPPER_CASE | `WLED_MAX_USERMODS`, `FX_MODE_STATIC` |\n| Private members | _camelCase | `_type`, `_bri`, `_len` |\n| Enum values | PascalCase | `PinOwner::BusDigital` |\n\n### Includes\n- Include `\"wled.h\"` as the primary project header\n- Project headers first, then platform/Arduino, then third-party\n- Platform-conditional includes wrapped in `#ifdef ARDUINO_ARCH_ESP32` / `#ifdef ESP8266`\n\n### Types and Const\n- Prefer `const &` for read-only function parameters\n- Mark getter/query methods `const`; use `static` for methods not accessing instance state\n- Prefer `constexpr` over `#define` for compile-time constants when possible\n- Use `static_assert` over `#if ... #error`\n- Use `uint_fast16_t` / `uint_fast8_t` in hot-path code\n\n### Error Handling\n- **No C++ exceptions** — some builds disable them\n- Use return codes (`false`, `-1`) and global flags (`errorFlag = ERR_LOW_MEM`)\n- Use early returns as guard clauses: `if (!enabled || (strip.isUpdating() && (millis() - last_time < MAX_USERMOD_DELAY))) return;`\n- Debug output: `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` (compiled out unless `-D WLED_DEBUG`)\n\n### Strings and Memory\n- Use `F(\"string\")` for string constants (saves RAM on ESP8266)\n- Use `PSTR()` with `DEBUG_PRINTF_P()` for format strings\n- Avoid `String` in hot paths; acceptable in config/setup code\n- Use `d_malloc()` (DRAM-preferred) / `p_malloc()` (PSRAM-preferred) for allocation\n- No VLAs — use fixed arrays or heap allocation\n- Call `reserve()` on strings/vectors to pre-allocate and avoid fragmentation\n\n#### ESP32 PSRAM guidelines\n\n- **Check availability**: Test chip availability with `psramFound() && ESP.getPsramSize() > 0` before assuming PSRAM is present. Never rely on `BOARD_HAS_PSRAM`only.\n- **DMA compatibility**: on ESP32 (classic), PSRAM buffers are **not DMA-capable**. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), PSRAM buffers *can* be used with DMA when `CONFIG_SOC_PSRAM_DMA_CAPABLE` is defined.\n- **Fragmentation**: PSRAM allocations fragment less than DRAM because the region is larger. But avoid mixing small and large allocations in PSRAM — small allocations waste the MMU page granularity.\n- **Performance**: Prefer DRAM (or IRAM) for hot-path data that is *frequently* used. Prefer PSRAM for capacity-oriented buffers where slightly slower access times can be tolerated.\n\nBackground Info:\n\n- PSRAM access is up to 15× slower than DRAM on ESP32 (dual-SPI bus), 3–10× slower than DRAM on ESP32-S3/-S2 with quad-SPI bus. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), the penalty is smaller (~2×) because the 8-line DTR bus can transfer 8 bits in parallel. On ESP32-P4 with hex PSRAM (`CONFIG_SPIRAM_MODE_HEX`), the 16-line bus runs at 200 MHz which brings it on-par with DRAM.\n- Consider that ESP32 often crashes when the largest available DRAM chunk gets below 10 KB.\n\n### Preprocessor / Feature Flags\n- Feature toggling: `WLED_DISABLE_*` and `WLED_ENABLE_*` flags (exact names matter!)\n- `WLED_DISABLE_*`: `2D`, `ADALIGHT`, `ALEXA`, `MQTT`, `OTA`, `INFRARED`, `WEBSOCKETS`, etc.\n- `WLED_ENABLE_*`: `DMX`, `GIF`, `HUB75MATRIX`, `JSONLIVE`, `WEBSOCKETS`, etc.\n- Platform: `ARDUINO_ARCH_ESP32`, `ESP8266`, `CONFIG_IDF_TARGET_ESP32S3`\n\n### Math Functions\n- Use `sin8_t()`, `cos8_t()` — NOT `sin8()`, `cos8()` (removed, won't compile)\n- Use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`\n- Replace `inoise8` / `inoise16` with `perlin8` / `perlin16`\n\n### Hot-Path Code (Pixel Pipeline)\n- Use function attributes: `IRAM_ATTR`, `WLED_O2_ATTR`, `__attribute__((hot))`\n- Cache class members to locals before loops\n- Pre-compute invariants outside loops; use reciprocals to avoid division\n- Unsigned range checks: `if ((uint_fast16_t)(pix - start) < len)`\n\n### ESP32 Tasks\n- `delay(1)` in custom FreeRTOS tasks (NOT `yield()`) — feeds IDLE watchdog\n- Do not use `delay()` in effects (FX.cpp) or hot pixel path\n\n#### ESP32 Task Synchronization\n\n- Use FreeRTOS mutexes, semaphores or queues when true concurrent access from multiple FreeRTOS tasks is possible, and race-conditions can lead to unexpected behaviour.\n- **Avoid `portENTER_CRITICAL()` / `portEXIT_CRITICAL()`**, as these functions stall the complete system and may cause LEDs flickering. Prefer FreeRTOS mutexes, semaphores or queues.\n- Don't use `portMAX_DELAY` when waiting to acquire a mutex - this can lock the task indefinitely. Find a reasonable max waiting time, and handle mutex timeouts gracefully.\n- **Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow, for example when function calls are sequenced within the same loop iteration.\n- Consider using `std::atomic` or RAII scoped guards as alternatives to mutexes, semaphores or queues.\n\n## Web UI Code Style (wled00/data/)\n\n- **Tab indentation** for HTML, JS, and CSS\n- camelCase for JS functions/variables\n- Reuse helpers from `common.js` — do not duplicate utilities\n- After editing, run `npm run build` to regenerate headers\n- **Never edit** `wled00/html_*.h` or `wled00/js_*.h` directly\n\n## Usermods\n \n### Source Code Location\n\n* **In-Tree Usermods** live in `usermods/<name>/` with a `.cpp`, optional `.h`, `library.json`, and `readme.md`.  An example is in `usermods/EXAMPLE`\n* **Out-Of-Tree Usermods** live in a separate public repository. They use the same pattern as in-tree usermods.\n\n* [Official out-of-tree usermods list](https://kno.wled.ge/advanced/community-usermods/#index)\n* [Writing an out-of-tree usermod](https://kno.wled.ge/advanced/custom-features/#writing-a-usermod)\n\n### Usermod Pattern\n\n```cpp\nclass MyUsermod : public Usermod {\n  private:\n    bool enabled = false;\n    static const char _name[];\n  public:\n    void setup() override { /* ... */ }                          // runs once at start-up\n    void loop() override { /* ... */ }                           // runs once per main loop iteration\n    void addToConfig(JsonObject& root) override { /* ... */ }    // create/add persistent settings (usermod settings)\n    bool readFromConfig(JsonObject& root) override { /* ... */ } // read from persistent settings (usermod settings UI)\n    uint16_t getId() override { return USERMOD_ID_MYMOD; }\n    void addToJsonInfo(JsonObject& root) override { /* ... */ }  // Add custom items to the \"info\" page and to /json/info\n    void appendConfigData() override { /* ... */ }               // Customize the settings page: dropdowns, checkboxes, extra text, etc. Buffer size is limited!\n};\nconst char MyUsermod::_name[] PROGMEM = \"MyUsermod\";\nstatic MyUsermod myUsermod;\nREGISTER_USERMOD(myUsermod);\n```\n\nrefer to detailed examples in `usermods/EXAMPLE/`, `usermods/user_fx/` and [in the user documentation for custom features](https://kno.wled.ge/advanced/custom-features/).\n\n- Activate via `custom_usermods = ` in platformio build config. The `usermod_v2_` prefix or `_v2` suffix can be omitted.\n- Base new usermods on `usermods/EXAMPLE/` (never edit the example directly)\n- Store repeated strings as `static const char[] PROGMEM`\n- Add usermod IDs to `wled00/const.h` **only when a unique ID is required** (see below)\n\n### Usermod IDs\n\nA unique ID (registered in `wled00/const.h` and overriding `getId()`) is **only required** when a usermod needs one or more of the following:\n\n1. **Inter-usermod communication** — another usermod or an FX effect calls `UsermodManager::lookup(mod_id)` or `UsermodManager::getUMData(..., mod_id)` to find or request data from this specific usermod.\n2. **Pin ownership via `pinManager`** — the usermod allocates GPIO pins through `pinManager`. Pin ownership is tracked by `PinOwner` enum values that map directly to `USERMOD_ID_*` constants (see `wled00/pin_manager.h`). This prevents pin-conflict bugs.\n3. **Identification in JSON info** — `UsermodManager::addToJsonInfo` emits each mod's ID into the `\"um\"` array; a unique ID makes the mod identifiable in that output.\n\nIf none of the above apply, the usermod may omit `getId()` (or return the default `USERMOD_ID_UNSPECIFIED`) and does **not** need an entry in `const.h`.\n\n### Usermod `loop()`\n\n- Called once per main loop iteration. Usermods should simply `return` when `!enabled`.\n- Frequency of calls varies with system load:\n    * up to 2000 times/sec with few LEDs and little background activity,\n    * between 20 and 300 times/second during high workload from effects and other usermods,\n    * (worst case) down to 1-3 times/sec during FS activity or when serving lots of network API requests.\n\n### See Also\n* https://kno.wled.ge/advanced/custom-features/#usermods\n* https://kno.wled.ge/advanced/community-usermods/#index\n\n## CI/CD\n\nCI runs on every push/PR via GitHub Actions (`.github/workflows/wled-ci.yml`):\n\n1. `npm test` (web UI build validation)\n2. Firmware compilation for all default environments (~22 targets)\n3. Post-link validation of usermod linkage (`validate_modules.py`)\n\nNo automated linting is configured. Match existing code style in files you edit.\n\n## General Rules\n\n- Important: Repository language is **English**. This applies to source code (including comments), commit messages and any kind of documentation for developer or users.\n- The `docs/` folder is for developer/contributor information (coding conventions, architecture, etc.). User documentation is maintained in the [wled/WLED-Docs](https://github.com/wled/WLED-Docs) repository.\n- Never edit or commit auto-generated `wled00/html_*.h` / `wled00/js_*.h`.\n- When updating an existing PR, retain the original description. Only modify it to ensure technical accuracy. Add change logs after the existing description.\n- No force-push on open PRs!\n- Important: **Changes to `platformio.ini` require maintainer approval**!\n- PRs should respect `.gitignore` and not upload files like  `platformio_override.ini`. PR authors may add buildenv examples for custom boards into `platformio_override.ini.sample`.\n- Remove dead/unused code — justify or delete it.\n- Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor).\n- Provide references when making analyses or recommendations. Support factual claims with verifiable citations, references or concrete evidence; **never fabricate citations**.\n- **Highlight user-visible breaking changes and ripple effects** during reviews. Ask for confirmation that these were introduced intentionally.\n\n### Security Hardening\n\nWhen writing or reviewing code in `wled00/`, `usermods/`, `wled00/data/`, or `.github/workflows/`,\nconsult `docs/hardening.instructions.md` (concise checklist) and `docs/securecode.instructions.md` (detailed rules with examples).\nThese files define WLED's threat model, trust boundary model, and WLED-specific constraints (no TLS baseline, no UDP authentication for protocol-defined\nmulticast/broadcast, firewall-isolated deployment assumed).\n\n### Attribution for AI-generated code\n\nUsing AI-generated code can hide the source of the inspiration / knowledge / sources it used.\n\n- Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used.\n- When a larger block of code is generated by an AI tool, embed it into `// AI: below section was generated by an AI` ... `// AI: end` comments (see Comments section).\n- Every non-trivial AI-generated function should have a brief comment describing what it does. Explain parameters when their names alone are not self-explanatory.\n- AI-generated code must be well documented with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning.\n\n### Supporting Reviews and Discussions\n\n- **For \"is it worth doing?\" debates** about proposed reliability, safety, or data-integrity mechanisms (CRC checks, backups, power-loss protection): suggest a software **FMEA** (Failure Mode and Effects Analysis).\n  Clarify the main feared events, enumerate failure modes, assess each mitigation's effectiveness per failure mode, note common-cause failures, and rate credibility for the typical WLED use case.\n"},"files":{"AGENTS.md":"# AGENTS.md — WLED AI Coding Agent & AI Code Review Reference\n\nWLED is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable LEDs,\nwith a web UI (HTML/JS/CSS). Built with PlatformIO (Arduino framework) and Node.js tooling.\n\nSee also: `.github/copilot-instructions.md`, `.github/agent-build.instructions.md`,\n`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`,\n`docs/hardening.instructions.md`, `docs/securecode.instructions.md`.\n\nAlways reference these instructions - including coding guidelines in `docs/` - first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.\n\n## Build Commands\n\n| Command | Purpose | Timeout |\n|---|---|---|\n| `npm ci` | Install Node.js deps (required first) | 30s |\n| `npm run build` | Build web UI into `wled00/html_*.h` / `wled00/js_*.h` | 30s |\n| `npm test` | Run test suite (Node.js built-in `node --test`) | 2 min |\n| `npm run dev` | Watch mode — auto-rebuilds web UI on changes | continuous |\n| `pio run -e esp32dev` | Build firmware (ESP32, most common target) | 5 min |\n| `pio run -e nodemcuv2` | Build firmware (ESP8266) | 5 min |\n\n**Always run `npm ci && npm run build` before `pio run`.** The web UI build generates\nrequired C headers for firmware compilation.\n\n### Running a Single Test\n\nTests use Node.js built-in test runner (`node:test`). The single test file is\n`tools/cdata-test.js`. Run it with:\n\n```bash\nnpm test                   # runs all tests via `node --test`\nnode --test tools/cdata-test.js  # run just that file directly\n```\n\nThere are no C++ unit tests. Firmware is validated by successful compilation across\ntarget environments. Always build after code changes: `pio run -e esp32dev`.\n\n### Common Firmware Environments\n\n`esp32dev`, `nodemcuv2`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi`, `lolin_s2_mini`\n\n### Recovery / Troubleshooting\n\n```bash\nnpm run build -- -f              # force web UI rebuild\nrm -f wled00/html_*.h wled00/js_*.h && npm run build  # clean + rebuild UI\npio run --target clean           # clean PlatformIO build artifacts\nrm -rf node_modules && npm ci    # reinstall Node.js deps\n```\n\n## Project Structure\n\n```text\nwled00/              # Main firmware source (C++)\n  data/              # Web UI source (HTML/JS/CSS) — tabs for indentation\n  html_*.h, js_*.h   # Auto-generated (NEVER edit or commit)\n  src/               # Sub-modules: fonts, bundled dependencies (ArduinoJSON)\nusermods/            # Community usermods (each has library.json + .cpp/.h)\nplatformio.ini       # Build configuration and environments\npio-scripts/         # PlatformIO build scripts (Python)\ntools/               # Node.js build tools (cdata.js) and tests\ndocs/                # Coding convention docs\n.github/workflows/   # CI/CD (GitHub Actions)\n```\n\n### Branch / Release Structure\n\n```text\nmain                # Main development trunk (daily/nightly) 17.0.0-dev. Target branch for PRs.\n  ├── V5            # special branch: code rework for esp-idf 5.5.x and new MCU types: esp32-c5, esp32-c6, esp32-p4 (unstable)\n16_x                # maintenance for release 16.0.x\n0_15_x              # maintenance (bugfixes only) for previous release 0.15.x\n(tag) v0.14.4       # old version 0.14.4 (no maintenance)\n(tag) v0.13.3       # old version 0.13.3 (no maintenance)\n(tag) v0. ... . ... # historical versions 0.12.x and before\n```\n\n## C++ Code Style (wled00/, usermods/)\n\n### Formatting\n- **2-space indentation** (no tabs in C++ files)\n- K&R brace style preferred (opening brace on same line)\n- Single-statement `if` bodies may omit braces: `if (a == b) doStuff(a);`\n- Space after keywords (`if (...)`, `for (...)`), no space before function parens (`doStuff(a)`)\n- No enforced line-length limit\n\n### Comments\n- `//` for inline (always space after), `/* */` for block comments\n- Important: AI-generated source code blocks **must be mark with `// AI: below section was generated by an AI` / `// AI: end`**\n\n### Naming Conventions\n| Kind | Convention | Examples |\n|---|---|---|\n| Functions, variables | camelCase | `setRandomColor()`, `effectCurrent` |\n| Classes, structs | PascalCase | `BusConfig`, `UsermodTemperature` |\n| Macros, constants | UPPER_CASE | `WLED_MAX_USERMODS`, `FX_MODE_STATIC` |\n| Private members | _camelCase | `_type`, `_bri`, `_len` |\n| Enum values | PascalCase | `PinOwner::BusDigital` |\n\n### Includes\n- Include `\"wled.h\"` as the primary project header\n- Project headers first, then platform/Arduino, then third-party\n- Platform-conditional includes wrapped in `#ifdef ARDUINO_ARCH_ESP32` / `#ifdef ESP8266`\n\n### Types and Const\n- Prefer `const &` for read-only function parameters\n- Mark getter/query methods `const`; use `static` for methods not accessing instance state\n- Prefer `constexpr` over `#define` for compile-time constants when possible\n- Use `static_assert` over `#if ... #error`\n- Use `uint_fast16_t` / `uint_fast8_t` in hot-path code\n\n### Error Handling\n- **No C++ exceptions** — some builds disable them\n- Use return codes (`false`, `-1`) and global flags (`errorFlag = ERR_LOW_MEM`)\n- Use early returns as guard clauses: `if (!enabled || (strip.isUpdating() && (millis() - last_time < MAX_USERMOD_DELAY))) return;`\n- Debug output: `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` (compiled out unless `-D WLED_DEBUG`)\n\n### Strings and Memory\n- Use `F(\"string\")` for string constants (saves RAM on ESP8266)\n- Use `PSTR()` with `DEBUG_PRINTF_P()` for format strings\n- Avoid `String` in hot paths; acceptable in config/setup code\n- Use `d_malloc()` (DRAM-preferred) / `p_malloc()` (PSRAM-preferred) for allocation\n- No VLAs — use fixed arrays or heap allocation\n- Call `reserve()` on strings/vectors to pre-allocate and avoid fragmentation\n\n#### ESP32 PSRAM guidelines\n\n- **Check availability**: Test chip availability with `psramFound() && ESP.getPsramSize() > 0` before assuming PSRAM is present. Never rely on `BOARD_HAS_PSRAM`only.\n- **DMA compatibility**: on ESP32 (classic), PSRAM buffers are **not DMA-capable**. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), PSRAM buffers *can* be used with DMA when `CONFIG_SOC_PSRAM_DMA_CAPABLE` is defined.\n- **Fragmentation**: PSRAM allocations fragment less than DRAM because the region is larger. But avoid mixing small and large allocations in PSRAM — small allocations waste the MMU page granularity.\n- **Performance**: Prefer DRAM (or IRAM) for hot-path data that is *frequently* used. Prefer PSRAM for capacity-oriented buffers where slightly slower access times can be tolerated.\n\nBackground Info:\n\n- PSRAM access is up to 15× slower than DRAM on ESP32 (dual-SPI bus), 3–10× slower than DRAM on ESP32-S3/-S2 with quad-SPI bus. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), the penalty is smaller (~2×) because the 8-line DTR bus can transfer 8 bits in parallel. On ESP32-P4 with hex PSRAM (`CONFIG_SPIRAM_MODE_HEX`), the 16-line bus runs at 200 MHz which brings it on-par with DRAM.\n- Consider that ESP32 often crashes when the largest available DRAM chunk gets below 10 KB.\n\n### Preprocessor / Feature Flags\n- Feature toggling: `WLED_DISABLE_*` and `WLED_ENABLE_*` flags (exact names matter!)\n- `WLED_DISABLE_*`: `2D`, `ADALIGHT`, `ALEXA`, `MQTT`, `OTA`, `INFRARED`, `WEBSOCKETS`, etc.\n- `WLED_ENABLE_*`: `DMX`, `GIF`, `HUB75MATRIX`, `JSONLIVE`, `WEBSOCKETS`, etc.\n- Platform: `ARDUINO_ARCH_ESP32`, `ESP8266`, `CONFIG_IDF_TARGET_ESP32S3`\n\n### Math Functions\n- Use `sin8_t()`, `cos8_t()` — NOT `sin8()`, `cos8()` (removed, won't compile)\n- Use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`\n- Replace `inoise8` / `inoise16` with `perlin8` / `perlin16`\n\n### Hot-Path Code (Pixel Pipeline)\n- Use function attributes: `IRAM_ATTR`, `WLED_O2_ATTR`, `__attribute__((hot))`\n- Cache class members to locals before loops\n- Pre-compute invariants outside loops; use reciprocals to avoid division\n- Unsigned range checks: `if ((uint_fast16_t)(pix - start) < len)`\n\n### ESP32 Tasks\n- `delay(1)` in custom FreeRTOS tasks (NOT `yield()`) — feeds IDLE watchdog\n- Do not use `delay()` in effects (FX.cpp) or hot pixel path\n\n#### ESP32 Task Synchronization\n\n- Use FreeRTOS mutexes, semaphores or queues when true concurrent access from multiple FreeRTOS tasks is possible, and race-conditions can lead to unexpected behaviour.\n- **Avoid `portENTER_CRITICAL()` / `portEXIT_CRITICAL()`**, as these functions stall the complete system and may cause LEDs flickering. Prefer FreeRTOS mutexes, semaphores or queues.\n- Don't use `portMAX_DELAY` when waiting to acquire a mutex - this can lock the task indefinitely. Find a reasonable max waiting time, and handle mutex timeouts gracefully.\n- **Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow, for example when function calls are sequenced within the same loop iteration.\n- Consider using `std::atomic` or RAII scoped guards as alternatives to mutexes, semaphores or queues.\n\n## Web UI Code Style (wled00/data/)\n\n- **Tab indentation** for HTML, JS, and CSS\n- camelCase for JS functions/variables\n- Reuse helpers from `common.js` — do not duplicate utilities\n- After editing, run `npm run build` to regenerate headers\n- **Never edit** `wled00/html_*.h` or `wled00/js_*.h` directly\n\n## Usermods\n \n### Source Code Location\n\n* **In-Tree Usermods** live in `usermods/<name>/` with a `.cpp`, optional `.h`, `library.json`, and `readme.md`.  An example is in `usermods/EXAMPLE`\n* **Out-Of-Tree Usermods** live in a separate public repository. They use the same pattern as in-tree usermods.\n\n* [Official out-of-tree usermods list](https://kno.wled.ge/advanced/community-usermods/#index)\n* [Writing an out-of-tree usermod](https://kno.wled.ge/advanced/custom-features/#writing-a-usermod)\n\n### Usermod Pattern\n\n```cpp\nclass MyUsermod : public Usermod {\n  private:\n    bool enabled = false;\n    static const char _name[];\n  public:\n    void setup() override { /* ... */ }                          // runs once at start-up\n    void loop() override { /* ... */ }                           // runs once per main loop iteration\n    void addToConfig(JsonObject& root) override { /* ... */ }    // create/add persistent settings (usermod settings)\n    bool readFromConfig(JsonObject& root) override { /* ... */ } // read from persistent settings (usermod settings UI)\n    uint16_t getId() override { return USERMOD_ID_MYMOD; }\n    void addToJsonInfo(JsonObject& root) override { /* ... */ }  // Add custom items to the \"info\" page and to /json/info\n    void appendConfigData() override { /* ... */ }               // Customize the settings page: dropdowns, checkboxes, extra text, etc. Buffer size is limited!\n};\nconst char MyUsermod::_name[] PROGMEM = \"MyUsermod\";\nstatic MyUsermod myUsermod;\nREGISTER_USERMOD(myUsermod);\n```\n\nrefer to detailed examples in `usermods/EXAMPLE/`, `usermods/user_fx/` and [in the user documentation for custom features](https://kno.wled.ge/advanced/custom-features/).\n\n- Activate via `custom_usermods = ` in platformio build config. The `usermod_v2_` prefix or `_v2` suffix can be omitted.\n- Base new usermods on `usermods/EXAMPLE/` (never edit the example directly)\n- Store repeated strings as `static const char[] PROGMEM`\n- Add usermod IDs to `wled00/const.h` **only when a unique ID is required** (see below)\n\n### Usermod IDs\n\nA unique ID (registered in `wled00/const.h` and overriding `getId()`) is **only required** when a usermod needs one or more of the following:\n\n1. **Inter-usermod communication** — another usermod or an FX effect calls `UsermodManager::lookup(mod_id)` or `UsermodManager::getUMData(..., mod_id)` to find or request data from this specific usermod.\n2. **Pin ownership via `pinManager`** — the usermod allocates GPIO pins through `pinManager`. Pin ownership is tracked by `PinOwner` enum values that map directly to `USERMOD_ID_*` constants (see `wled00/pin_manager.h`). This prevents pin-conflict bugs.\n3. **Identification in JSON info** — `UsermodManager::addToJsonInfo` emits each mod's ID into the `\"um\"` array; a unique ID makes the mod identifiable in that output.\n\nIf none of the above apply, the usermod may omit `getId()` (or return the default `USERMOD_ID_UNSPECIFIED`) and does **not** need an entry in `const.h`.\n\n### Usermod `loop()`\n\n- Called once per main loop iteration. Usermods should simply `return` when `!enabled`.\n- Frequency of calls varies with system load:\n    * up to 2000 times/sec with few LEDs and little background activity,\n    * between 20 and 300 times/second during high workload from effects and other usermods,\n    * (worst case) down to 1-3 times/sec during FS activity or when serving lots of network API requests.\n\n### See Also\n* https://kno.wled.ge/advanced/custom-features/#usermods\n* https://kno.wled.ge/advanced/community-usermods/#index\n\n## CI/CD\n\nCI runs on every push/PR via GitHub Actions (`.github/workflows/wled-ci.yml`):\n\n1. `npm test` (web UI build validation)\n2. Firmware compilation for all default environments (~22 targets)\n3. Post-link validation of usermod linkage (`validate_modules.py`)\n\nNo automated linting is configured. Match existing code style in files you edit.\n\n## General Rules\n\n- Important: Repository language is **English**. This applies to source code (including comments), commit messages and any kind of documentation for developer or users.\n- The `docs/` folder is for developer/contributor information (coding conventions, architecture, etc.). User documentation is maintained in the [wled/WLED-Docs](https://github.com/wled/WLED-Docs) repository.\n- Never edit or commit auto-generated `wled00/html_*.h` / `wled00/js_*.h`.\n- When updating an existing PR, retain the original description. Only modify it to ensure technical accuracy. Add change logs after the existing description.\n- No force-push on open PRs!\n- Important: **Changes to `platformio.ini` require maintainer approval**!\n- PRs should respect `.gitignore` and not upload files like  `platformio_override.ini`. PR authors may add buildenv examples for custom boards into `platformio_override.ini.sample`.\n- Remove dead/unused code — justify or delete it.\n- Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor).\n- Provide references when making analyses or recommendations. Support factual claims with verifiable citations, references or concrete evidence; **never fabricate citations**.\n- **Highlight user-visible breaking changes and ripple effects** during reviews. Ask for confirmation that these were introduced intentionally.\n\n### Security Hardening\n\nWhen writing or reviewing code in `wled00/`, `usermods/`, `wled00/data/`, or `.github/workflows/`,\nconsult `docs/hardening.instructions.md` (concise checklist) and `docs/securecode.instructions.md` (detailed rules with examples).\nThese files define WLED's threat model, trust boundary model, and WLED-specific constraints (no TLS baseline, no UDP authentication for protocol-defined\nmulticast/broadcast, firewall-isolated deployment assumed).\n\n### Attribution for AI-generated code\n\nUsing AI-generated code can hide the source of the inspiration / knowledge / sources it used.\n\n- Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used.\n- When a larger block of code is generated by an AI tool, embed it into `// AI: below section was generated by an AI` ... `// AI: end` comments (see Comments section).\n- Every non-trivial AI-generated function should have a brief comment describing what it does. Explain parameters when their names alone are not self-explanatory.\n- AI-generated code must be well documented with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning.\n\n### Supporting Reviews and Discussions\n\n- **For \"is it worth doing?\" debates** about proposed reliability, safety, or data-integrity mechanisms (CRC checks, backups, power-loss protection): suggest a software **FMEA** (Failure Mode and Effects Analysis).\n  Clarify the main feared events, enumerate failure modes, assess each mitigation's effectiveness per failure mode, note common-cause failures, and rate credibility for the typical WLED use case.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# AGENTS.md — WLED AI Coding Agent & AI Code Review Reference\n\nWLED is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable LEDs,\nwith a web UI (HTML/JS/CSS). Built with PlatformIO (Arduino framework) and Node.js tooling.\n\nSee also: `.github/copilot-instructions.md`, `.github/agent-build.instructions.md`,\n`docs/cpp.instructions.md`, `docs/web.instructions.md`, `docs/cicd.instructions.md`,\n`docs/hardening.instructions.md`, `docs/securecode.instructions.md`.\n\nAlways reference these instructions - including coding guidelines in `docs/` - first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.\n\n## Build Commands\n\n| Command | Purpose | Timeout |\n|---|---|---|\n| `npm ci` | Install Node.js deps (required first) | 30s |\n| `npm run build` | Build web UI into `wled00/html_*.h` / `wled00/js_*.h` | 30s |\n| `npm test` | Run test suite (Node.js built-in `node --test`) | 2 min |\n| `npm run dev` | Watch mode — auto-rebuilds web UI on changes | continuous |\n| `pio run -e esp32dev` | Build firmware (ESP32, most common target) | 5 min |\n| `pio run -e nodemcuv2` | Build firmware (ESP8266) | 5 min |\n\n**Always run `npm ci && npm run build` before `pio run`.** The web UI build generates\nrequired C headers for firmware compilation.\n\n### Running a Single Test\n\nTests use Node.js built-in test runner (`node:test`). The single test file is\n`tools/cdata-test.js`. Run it with:\n\n```bash\nnpm test                   # runs all tests via `node --test`\nnode --test tools/cdata-test.js  # run just that file directly\n```\n\nThere are no C++ unit tests. Firmware is validated by successful compilation across\ntarget environments. Always build after code changes: `pio run -e esp32dev`.\n\n### Common Firmware Environments\n\n`esp32dev`, `nodemcuv2`, `esp8266_2m`, `esp32c3dev`, `esp32s3dev_8MB_opi`, `lolin_s2_mini`\n\n### Recovery / Troubleshooting\n\n```bash\nnpm run build -- -f              # force web UI rebuild\nrm -f wled00/html_*.h wled00/js_*.h && npm run build  # clean + rebuild UI\npio run --target clean           # clean PlatformIO build artifacts\nrm -rf node_modules && npm ci    # reinstall Node.js deps\n```\n\n## Project Structure\n\n```text\nwled00/              # Main firmware source (C++)\n  data/              # Web UI source (HTML/JS/CSS) — tabs for indentation\n  html_*.h, js_*.h   # Auto-generated (NEVER edit or commit)\n  src/               # Sub-modules: fonts, bundled dependencies (ArduinoJSON)\nusermods/            # Community usermods (each has library.json + .cpp/.h)\nplatformio.ini       # Build configuration and environments\npio-scripts/         # PlatformIO build scripts (Python)\ntools/               # Node.js build tools (cdata.js) and tests\ndocs/                # Coding convention docs\n.github/workflows/   # CI/CD (GitHub Actions)\n```\n\n### Branch / Release Structure\n\n```text\nmain                # Main development trunk (daily/nightly) 17.0.0-dev. Target branch for PRs.\n  ├── V5            # special branch: code rework for esp-idf 5.5.x and new MCU types: esp32-c5, esp32-c6, esp32-p4 (unstable)\n16_x                # maintenance for release 16.0.x\n0_15_x              # maintenance (bugfixes only) for previous release 0.15.x\n(tag) v0.14.4       # old version 0.14.4 (no maintenance)\n(tag) v0.13.3       # old version 0.13.3 (no maintenance)\n(tag) v0. ... . ... # historical versions 0.12.x and before\n```\n\n## C++ Code Style (wled00/, usermods/)\n\n### Formatting\n- **2-space indentation** (no tabs in C++ files)\n- K&R brace style preferred (opening brace on same line)\n- Single-statement `if` bodies may omit braces: `if (a == b) doStuff(a);`\n- Space after keywords (`if (...)`, `for (...)`), no space before function parens (`doStuff(a)`)\n- No enforced line-length limit\n\n### Comments\n- `//` for inline (always space after), `/* */` for block comments\n- Important: AI-generated source code blocks **must be mark with `// AI: below section was generated by an AI` / `// AI: end`**\n\n### Naming Conventions\n| Kind | Convention | Examples |\n|---|---|---|\n| Functions, variables | camelCase | `setRandomColor()`, `effectCurrent` |\n| Classes, structs | PascalCase | `BusConfig`, `UsermodTemperature` |\n| Macros, constants | UPPER_CASE | `WLED_MAX_USERMODS`, `FX_MODE_STATIC` |\n| Private members | _camelCase | `_type`, `_bri`, `_len` |\n| Enum values | PascalCase | `PinOwner::BusDigital` |\n\n### Includes\n- Include `\"wled.h\"` as the primary project header\n- Project headers first, then platform/Arduino, then third-party\n- Platform-conditional includes wrapped in `#ifdef ARDUINO_ARCH_ESP32` / `#ifdef ESP8266`\n\n### Types and Const\n- Prefer `const &` for read-only function parameters\n- Mark getter/query methods `const`; use `static` for methods not accessing instance state\n- Prefer `constexpr` over `#define` for compile-time constants when possible\n- Use `static_assert` over `#if ... #error`\n- Use `uint_fast16_t` / `uint_fast8_t` in hot-path code\n\n### Error Handling\n- **No C++ exceptions** — some builds disable them\n- Use return codes (`false`, `-1`) and global flags (`errorFlag = ERR_LOW_MEM`)\n- Use early returns as guard clauses: `if (!enabled || (strip.isUpdating() && (millis() - last_time < MAX_USERMOD_DELAY))) return;`\n- Debug output: `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` (compiled out unless `-D WLED_DEBUG`)\n\n### Strings and Memory\n- Use `F(\"string\")` for string constants (saves RAM on ESP8266)\n- Use `PSTR()` with `DEBUG_PRINTF_P()` for format strings\n- Avoid `String` in hot paths; acceptable in config/setup code\n- Use `d_malloc()` (DRAM-preferred) / `p_malloc()` (PSRAM-preferred) for allocation\n- No VLAs — use fixed arrays or heap allocation\n- Call `reserve()` on strings/vectors to pre-allocate and avoid fragmentation\n\n#### ESP32 PSRAM guidelines\n\n- **Check availability**: Test chip availability with `psramFound() && ESP.getPsramSize() > 0` before assuming PSRAM is present. Never rely on `BOARD_HAS_PSRAM`only.\n- **DMA compatibility**: on ESP32 (classic), PSRAM buffers are **not DMA-capable**. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), PSRAM buffers *can* be used with DMA when `CONFIG_SOC_PSRAM_DMA_CAPABLE` is defined.\n- **Fragmentation**: PSRAM allocations fragment less than DRAM because the region is larger. But avoid mixing small and large allocations in PSRAM — small allocations waste the MMU page granularity.\n- **Performance**: Prefer DRAM (or IRAM) for hot-path data that is *frequently* used. Prefer PSRAM for capacity-oriented buffers where slightly slower access times can be tolerated.\n\nBackground Info:\n\n- PSRAM access is up to 15× slower than DRAM on ESP32 (dual-SPI bus), 3–10× slower than DRAM on ESP32-S3/-S2 with quad-SPI bus. On ESP32-S3 with octal PSRAM (`CONFIG_SPIRAM_MODE_OCT`), the penalty is smaller (~2×) because the 8-line DTR bus can transfer 8 bits in parallel. On ESP32-P4 with hex PSRAM (`CONFIG_SPIRAM_MODE_HEX`), the 16-line bus runs at 200 MHz which brings it on-par with DRAM.\n- Consider that ESP32 often crashes when the largest available DRAM chunk gets below 10 KB.\n\n### Preprocessor / Feature Flags\n- Feature toggling: `WLED_DISABLE_*` and `WLED_ENABLE_*` flags (exact names matter!)\n- `WLED_DISABLE_*`: `2D`, `ADALIGHT`, `ALEXA`, `MQTT`, `OTA`, `INFRARED`, `WEBSOCKETS`, etc.\n- `WLED_ENABLE_*`: `DMX`, `GIF`, `HUB75MATRIX`, `JSONLIVE`, `WEBSOCKETS`, etc.\n- Platform: `ARDUINO_ARCH_ESP32`, `ESP8266`, `CONFIG_IDF_TARGET_ESP32S3`\n\n### Math Functions\n- Use `sin8_t()`, `cos8_t()` — NOT `sin8()`, `cos8()` (removed, won't compile)\n- Use `sin_approx()` / `cos_approx()` instead of `sinf()` / `cosf()`\n- Replace `inoise8` / `inoise16` with `perlin8` / `perlin16`\n\n### Hot-Path Code (Pixel Pipeline)\n- Use function attributes: `IRAM_ATTR`, `WLED_O2_ATTR`, `__attribute__((hot))`\n- Cache class members to locals before loops\n- Pre-compute invariants outside loops; use reciprocals to avoid division\n- Unsigned range checks: `if ((uint_fast16_t)(pix - start) < len)`\n\n### ESP32 Tasks\n- `delay(1)` in custom FreeRTOS tasks (NOT `yield()`) — feeds IDLE watchdog\n- Do not use `delay()` in effects (FX.cpp) or hot pixel path\n\n#### ESP32 Task Synchronization\n\n- Use FreeRTOS mutexes, semaphores or queues when true concurrent access from multiple FreeRTOS tasks is possible, and race-conditions can lead to unexpected behaviour.\n- **Avoid `portENTER_CRITICAL()` / `portEXIT_CRITICAL()`**, as these functions stall the complete system and may cause LEDs flickering. Prefer FreeRTOS mutexes, semaphores or queues.\n- Don't use `portMAX_DELAY` when waiting to acquire a mutex - this can lock the task indefinitely. Find a reasonable max waiting time, and handle mutex timeouts gracefully.\n- **Important**: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow, for example when function calls are sequenced within the same loop iteration.\n- Consider using `std::atomic` or RAII scoped guards as alternatives to mutexes, semaphores or queues.\n\n## Web UI Code Style (wled00/data/)\n\n- **Tab indentation** for HTML, JS, and CSS\n- camelCase for JS functions/variables\n- Reuse helpers from `common.js` — do not duplicate utilities\n- After editing, run `npm run build` to regenerate headers\n- **Never edit** `wled00/html_*.h` or `wled00/js_*.h` directly\n\n## Usermods\n \n### Source Code Location\n\n* **In-Tree Usermods** live in `usermods/<name>/` with a `.cpp`, optional `.h`, `library.json`, and `readme.md`.  An example is in `usermods/EXAMPLE`\n* **Out-Of-Tree Usermods** live in a separate public repository. They use the same pattern as in-tree usermods.\n\n* [Official out-of-tree usermods list](https://kno.wled.ge/advanced/community-usermods/#index)\n* [Writing an out-of-tree usermod](https://kno.wled.ge/advanced/custom-features/#writing-a-usermod)\n\n### Usermod Pattern\n\n```cpp\nclass MyUsermod : public Usermod {\n  private:\n    bool enabled = false;\n    static const char _name[];\n  public:\n    void setup() override { /* ... */ }                          // runs once at start-up\n    void loop() override { /* ... */ }                           // runs once per main loop iteration\n    void addToConfig(JsonObject& root) override { /* ... */ }    // create/add persistent settings (usermod settings)\n    bool readFromConfig(JsonObject& root) override { /* ... */ } // read from persistent settings (usermod settings UI)\n    uint16_t getId() override { return USERMOD_ID_MYMOD; }\n    void addToJsonInfo(JsonObject& root) override { /* ... */ }  // Add custom items to the \"info\" page and to /json/info\n    void appendConfigData() override { /* ... */ }               // Customize the settings page: dropdowns, checkboxes, extra text, etc. Buffer size is limited!\n};\nconst char MyUsermod::_name[] PROGMEM = \"MyUsermod\";\nstatic MyUsermod myUsermod;\nREGISTER_USERMOD(myUsermod);\n```\n\nrefer to detailed examples in `usermods/EXAMPLE/`, `usermods/user_fx/` and [in the user documentation for custom features](https://kno.wled.ge/advanced/custom-features/).\n\n- Activate via `custom_usermods = ` in platformio build config. The `usermod_v2_` prefix or `_v2` suffix can be omitted.\n- Base new usermods on `usermods/EXAMPLE/` (never edit the example directly)\n- Store repeated strings as `static const char[] PROGMEM`\n- Add usermod IDs to `wled00/const.h` **only when a unique ID is required** (see below)\n\n### Usermod IDs\n\nA unique ID (registered in `wled00/const.h` and overriding `getId()`) is **only required** when a usermod needs one or more of the following:\n\n1. **Inter-usermod communication** — another usermod or an FX effect calls `UsermodManager::lookup(mod_id)` or `UsermodManager::getUMData(..., mod_id)` to find or request data from this specific usermod.\n2. **Pin ownership via `pinManager`** — the usermod allocates GPIO pins through `pinManager`. Pin ownership is tracked by `PinOwner` enum values that map directly to `USERMOD_ID_*` constants (see `wled00/pin_manager.h`). This prevents pin-conflict bugs.\n3. **Identification in JSON info** — `UsermodManager::addToJsonInfo` emits each mod's ID into the `\"um\"` array; a unique ID makes the mod identifiable in that output.\n\nIf none of the above apply, the usermod may omit `getId()` (or return the default `USERMOD_ID_UNSPECIFIED`) and does **not** need an entry in `const.h`.\n\n### Usermod `loop()`\n\n- Called once per main loop iteration. Usermods should simply `return` when `!enabled`.\n- Frequency of calls varies with system load:\n    * up to 2000 times/sec with few LEDs and little background activity,\n    * between 20 and 300 times/second during high workload from effects and other usermods,\n    * (worst case) down to 1-3 times/sec during FS activity or when serving lots of network API requests.\n\n### See Also\n* https://kno.wled.ge/advanced/custom-features/#usermods\n* https://kno.wled.ge/advanced/community-usermods/#index\n\n## CI/CD\n\nCI runs on every push/PR via GitHub Actions (`.github/workflows/wled-ci.yml`):\n\n1. `npm test` (web UI build validation)\n2. Firmware compilation for all default environments (~22 targets)\n3. Post-link validation of usermod linkage (`validate_modules.py`)\n\nNo automated linting is configured. Match existing code style in files you edit.\n\n## General Rules\n\n- Important: Repository language is **English**. This applies to source code (including comments), commit messages and any kind of documentation for developer or users.\n- The `docs/` folder is for developer/contributor information (coding conventions, architecture, etc.). User documentation is maintained in the [wled/WLED-Docs](https://github.com/wled/WLED-Docs) repository.\n- Never edit or commit auto-generated `wled00/html_*.h` / `wled00/js_*.h`.\n- When updating an existing PR, retain the original description. Only modify it to ensure technical accuracy. Add change logs after the existing description.\n- No force-push on open PRs!\n- Important: **Changes to `platformio.ini` require maintainer approval**!\n- PRs should respect `.gitignore` and not upload files like  `platformio_override.ini`. PR authors may add buildenv examples for custom boards into `platformio_override.ini.sample`.\n- Remove dead/unused code — justify or delete it.\n- Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor).\n- Provide references when making analyses or recommendations. Support factual claims with verifiable citations, references or concrete evidence; **never fabricate citations**.\n- **Highlight user-visible breaking changes and ripple effects** during reviews. Ask for confirmation that these were introduced intentionally.\n\n### Security Hardening\n\nWhen writing or reviewing code in `wled00/`, `usermods/`, `wled00/data/`, or `.github/workflows/`,\nconsult `docs/hardening.instructions.md` (concise checklist) and `docs/securecode.instructions.md` (detailed rules with examples).\nThese files define WLED's threat model, trust boundary model, and WLED-specific constraints (no TLS baseline, no UDP authentication for protocol-defined\nmulticast/broadcast, firewall-isolated deployment assumed).\n\n### Attribution for AI-generated code\n\nUsing AI-generated code can hide the source of the inspiration / knowledge / sources it used.\n\n- Document attribution of inspiration / knowledge / sources used in the code, e.g. link to GitHub repositories or other websites describing the principles / algorithms used.\n- When a larger block of code is generated by an AI tool, embed it into `// AI: below section was generated by an AI` ... `// AI: end` comments (see Comments section).\n- Every non-trivial AI-generated function should have a brief comment describing what it does. Explain parameters when their names alone are not self-explanatory.\n- AI-generated code must be well documented with meaningful comments that explain intent, assumptions, and non-obvious logic. Do not rephrase source code; explain concepts and reasoning.\n\n### Supporting Reviews and Discussions\n\n- **For \"is it worth doing?\" debates** about proposed reliability, safety, or data-integrity mechanisms (CRC checks, backups, power-loss protection): suggest a software **FMEA** (Failure Mode and Effects Analysis).\n  Clarify the main feared events, enumerate failure modes, assess each mitigation's effectiveness per failure mode, note common-cause failures, and rate credibility for the typical WLED use case.\n","category":"root","tokens":4071}]}