Fix agent (Cursor) rules
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
---
|
||||
description: DigiRadio core non-negotiables (always on)
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# DigiRadio — core rules
|
||||
|
||||
Firmware for ESP32-S3 on ESP-IDF v5.5.x. C++23 (-std=gnu++23),
|
||||
strongly typed, class-based. C++ exceptions disabled. Companion chips:
|
||||
Si4684 (DAB+/FM), ADAU1701 (SigmaDSP), FSC-BT1035 (Bluetooth).
|
||||
Full spec: @AGENTS.md
|
||||
|
||||
## Behaviour
|
||||
- Blockers first: state what breaks the build or the hardware before the
|
||||
solution.
|
||||
- Never invent a register address, opcode, bit field, or boot sequence.
|
||||
If it is not in the datasheet, say so and stop. Cite the doc section.
|
||||
- No silent failure: every fallible op returns a typed error.
|
||||
- Ask when a hardware invariant is unclear — don't assume.
|
||||
|
||||
## Code That Fits in Your Head (hard limits)
|
||||
- Cyclomatic complexity <= 7 per method; at 8, decompose.
|
||||
- Method fits an 80x24 box: <= 80 cols wide, <= 24 lines tall.
|
||||
- One method does one thing at one level of abstraction.
|
||||
- Name for intent (`tuneTo`), not mechanism (`writeReg0x30`).
|
||||
- Delete before you add.
|
||||
|
||||
## Errors
|
||||
- Typed result `std::expected<T, Error>` (native under C++23), never a
|
||||
bare int code. Every timeout is an explicit error value. No C++
|
||||
exceptions (disabled in ESP-IDF).
|
||||
|
||||
## Embedded
|
||||
- No dynamic allocation in audio or ISR paths, ever.
|
||||
- No virtual calls in IRAM-safe ISRs (vtables live in flash).
|
||||
- Every wait has a timeout and a defined failure path.
|
||||
|
||||
## Version control
|
||||
- Small commits, each compiles and keeps tests green.
|
||||
- Commit messages: 50/72 (summary <= 50 chars, body wrapped at 72).
|
||||
|
||||
## Definition of Done (summary — full list in @AGENTS.md §10)
|
||||
Compiles warnings-as-errors; clang-tidy clean; Apache header on every
|
||||
file; doc block on every class/method; `doxygen Doxyfile` exits 0;
|
||||
typed errors; host tests green; no plaintext secrets.
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
description: C++ typing, style, file headers and mandatory doc blocks
|
||||
globs: **/*.hpp, **/*.h, **/*.cpp
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# C++ typing, headers and documentation
|
||||
|
||||
Full spec: @AGENTS.md §2 and §3.
|
||||
|
||||
## Typing
|
||||
- No primitive obsession: a frequency, a gain, a station id are their own
|
||||
types, not int/float/uint8_t. Validate at construction.
|
||||
- `enum class` always; never a bare enum. No bool for mode selection
|
||||
(`setBand(Band::Dab)`, not `setBand(true)`).
|
||||
- Parse untrusted input (network, UART, flash) once at the boundary into
|
||||
a domain type; trust it downstream.
|
||||
- `const` by default; `[[nodiscard]]` on status/value returns.
|
||||
- Rule of zero: wrap every HW/OS handle in RAII. No raw new/delete.
|
||||
- Borrow with `std::span`, never pointer+length.
|
||||
- Command Query Separation. Functional core (pure, host-testable) /
|
||||
imperative shell (all I2C/SPI/UART/flash). Core includes no ESP-IDF.
|
||||
|
||||
## File header (every .hpp/.cpp) — Apache-2.0
|
||||
```cpp
|
||||
/**
|
||||
* @file <name>
|
||||
* @brief <one line>
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date <YYYY-MM-DD of creation>
|
||||
*/
|
||||
```
|
||||
|
||||
## Doc block — every class AND every method
|
||||
Fields, in order, using Doxygen tags (aliases @dname/@pubstate defined
|
||||
in Doxyfile):
|
||||
```cpp
|
||||
/**
|
||||
* @brief <name> — one-line intent.
|
||||
*
|
||||
* @dname <name>
|
||||
* @param <p> <meaning> // per parameter; none -> "none"
|
||||
* @return <success + each error cause> // or void / n/a
|
||||
* @pubstate <member state read/written; injected deps used; or none>
|
||||
*
|
||||
* Description: intent and contract, NOT a restatement of the code.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date <YYYY-MM-DD>
|
||||
*/
|
||||
```
|
||||
`doxygen Doxyfile` must exit 0 with an empty warnings log. Do not use
|
||||
`EXTRACT_ALL = YES` to silence missing-doc warnings.
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
description: Chip driver rules (Si4684, ADAU1701, FSC-BT1035)
|
||||
globs: **/*Driver.*, **/drivers/**, **/*Adau*, **/*Si4684*, **/*Bt1035*
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Chip drivers
|
||||
|
||||
Full spec: @AGENTS.md §7.1–7.3.
|
||||
|
||||
## Si4684 (DAB+/FM tuner)
|
||||
- Boot flow POWER_UP -> load patch -> load image -> BOOT must follow the
|
||||
AN649 sequence exactly; cite the section per step in a comment.
|
||||
- Stream firmware images in bounded chunks from flash via an injected
|
||||
`IFirmwareSource`; never load a whole image into a heap buffer.
|
||||
- Opcodes/property IDs are `enum class`; validate the CTS/STATUS byte
|
||||
before trusting any payload. Public API is intent-level; registers
|
||||
are private. One driver owns one SPI/I2C handle via RAII.
|
||||
|
||||
## ADAU1701 (SigmaDSP, RAM boot, no EEPROM)
|
||||
- ESP32 writes the SigmaStudio export to DSP RAM at every boot. Model the
|
||||
export as an ordered list of RegisterWrite{address, bytes} parsed in
|
||||
the pure core, replayed by the shell over I2C.
|
||||
- EQ and mixer runtime changes use safeload (click-free). A raw param
|
||||
write while audio runs is a bug.
|
||||
- Typed control surfaces: setEqBand(EqBandIndex, GainDb, FrequencyHz, Q);
|
||||
setInputMix(MixSource, GainDb) with enum class MixSource {Si4684,Esp32}.
|
||||
- Biquad/gain math lives in the pure core with host tests vs reference.
|
||||
|
||||
## FSC-BT1035 (QCC3056, AT over UART)
|
||||
- Typed command builder; explicit OK/ERROR/timeout parsing.
|
||||
- AT+AUXCFG=1 (Line-In) is mandatory in the init sequence and covered by
|
||||
a test on the command string. Unknown responses are an error, not
|
||||
ignored.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
description: Network provisioning and web UI rules
|
||||
globs: **/net/**, **/network/**, **/web/**, **/ui/**, **/*.html, **/*.css, **/*.js
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Network config + Web UI
|
||||
|
||||
Full spec: @AGENTS.md §7.4.
|
||||
|
||||
- Provisioning: SoftAP/captive portal first, then STA. State machine is
|
||||
an explicit `enum class NetState` — no ad-hoc flags.
|
||||
- UI: minimal single-page app served gzipped from flash. No heavy
|
||||
frameworks. Design tokens (spacing, type scale, one accent) defined
|
||||
once and reused — consistency over decoration. The UI is a thin client
|
||||
over a typed JSON API and holds no business logic.
|
||||
- API: typed DTOs. Parse every request body into a domain type at the
|
||||
boundary before use; reject malformed input with a clear status; never
|
||||
partially apply.
|
||||
- No raw filesystem or debug endpoint in a shipping build (guard behind a
|
||||
build flag).
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
description: Secure storage and secret handling
|
||||
globs: **/*Secret*, **/*Store*, **/secure/**, **/*Credential*, **/*Config*
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Secure storage
|
||||
|
||||
Full spec: @AGENTS.md §7.5.
|
||||
|
||||
- Stores Wi-Fi SSID/password, user credentials, station list. Encrypted
|
||||
at rest (NVS encryption on an encrypted partition, or a device key in
|
||||
eFuse). Confirm the mechanism against current ESP-IDF security docs
|
||||
before implementing.
|
||||
- A `Secret` wrapper: no operator<<, no implicit conversion to a loggable
|
||||
string, buffer zeroised on destruction.
|
||||
- Secrets are never logged, never placed in URLs, never serialised to
|
||||
plaintext. Access goes through `ISecureStore` so core and tests never
|
||||
touch real flash or real keys.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
description: Testing conventions (host-first, TDD for the pure core)
|
||||
globs: **/test/**, **/tests/**, **/*Test*, **/*_test.*
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Testing
|
||||
|
||||
Full spec: @AGENTS.md §8.
|
||||
|
||||
- Pure core is developed test-first (red -> green -> refactor):
|
||||
coefficient math, blob framing, config parsing, station-list logic —
|
||||
all host-tested, zero hardware.
|
||||
- Arrange-Act-Assert; one behaviour per test; names state the behaviour
|
||||
(`tuneTo_rejectsFrequencyOutsideFmBand`).
|
||||
- Fakes over mocks for driver interfaces; assert on observable behaviour,
|
||||
not internal call order.
|
||||
- Hardware-in-the-loop tests are separate, explicitly marked, and never
|
||||
block the host suite.
|
||||
Reference in New Issue
Block a user