Remove the M5Stack Gray variant from the fleet
The M5Stack Gray is retired (owner-approved): the fleet drops from 9 to 8
device variants. This removes the Gray as a build target / device while
keeping every reusable driver and HAL layer intact.
Removed (Gray-exclusive wiring only):
- variants/m5stack_gray/ (variant.h, ini, sdkconfig, the four envs)
- src/boards/board_gray.cpp (the DEVICE_ID==5 board-registry factory)
- the web installer mockup (gray.svg + screen png) and app.js entry
- docs/hardware/devices/m5stack-gray.md and every fleet-list / inventory
/ identifier / secure-availability row across docs, CI, and instructions
Kept (per owner: preserve tested drivers/HAL even with no current consumer):
- the IP5306 PMIC driver family (src/drivers/power/ip5306*) — Gray was its
only board; still native-validated by test_small_pmic_regmaps
- the classic-ESP32 RTC_CNTL MMIO regmap (rtc_cntl_esp32_regmap.h) — Gray
was the only classic internal-RTC board; still native-validated
- board_gray_log_wrap.cpp renamed to board_log_printf_wrap.cpp: the
__wrap_log_printf stub is fleet-wide (esp32_base defines the gate), not
Gray-specific
DEVICE_ID 5 is retired and left unused so the remaining ids stay stable.
CI covering set rebalanced (Gray was the classic-ESP32 representative):
sticks3, tdeck, cores3_se, m5core_ink, m5stickc_plus2. m5stickc_plus2
(classic ESP32 + ST7789 full-frame polling + 8 MB OTA) replaces the Gray's
classic-ESP32 + OTA + polling coverage. core2_v13 is intentionally not added
to CI: it is an "Initial" WIP board that currently overflows DRAM at link
(pre-existing), so it cannot be a green gate member.
Native tests 2306/2306. Base envs build green for the new CI set
(sticks3, m5stickc_plus2, tdeck, cores3_se, m5core_ink).
Claude-Session: https://claude.ai/code/session_01P6BNTpbgrvnZXSJnNuj8ZJ
Right-size the flash layout and write the index once per import
Two capacity items that were blocking the raised per-variant caps.
The partition tables gave the app far more room than the largest image
needs while starving the vault: the 8 MB boards now run 2 x 0x2C0000 app
slots with a 0x260000 LittleFS, the 4 MB boards a 0x290000 app with
0x160000 of filesystem, and the 16 MB tables keep their geometry. The
loaded 8 MB build sits at 78 % of its app slot and the 4 MB ones near
70 %, so the headroom is real rather than asserted. Two rationales that
had gone stale — a ">1500 credentials" claim and an Arduino/ESP-TEE
headroom note — are gone.
Changing the layout means a device must be fully re-flashed and its vault
repopulated, which is the standing pre-release policy; the flashing and
variant docs now say so where someone upgrading would look.
The bulk import used to rewrite index.bin for every row, and each rewrite
materialized the whole capacity-scaled working set: at 500 credentials
that is a flash rewrite and ~89 KB of transient per credential. A batch
guard now defers the rows and rebuilds the index once at the end.
Deferral is only safe if a half-finished batch can never be mistaken for
a finished one, and review found three ways it could be:
- A transient key-derivation failure returned without closing the batch.
Every later index mutation then reported success while writing nothing,
favorites and ordering survived only in RAM, and the portal's
index-repair button reported success while doing nothing — until a
reboot. The batch now closes before the write, so a failed write cannot
leave mutators deferring into a buffer nobody will flush, and the
key-derivation path aborts the batch explicitly.
- If the pre-batch unlink failed — lfs_remove needs a metadata block, so
it fails on a full filesystem — the old index survived and the batch
deferred every row into oblivion, leaving a MAC-valid, complete-looking
index missing every imported credential with no path back. The batch
now refuses to open and per-row writes stand: slower, not wrong.
- The batch depth was atomic to survive the vault worker's inline-
execution fallback but the snapshot pointer was not, leaving a
use-after-free window in exactly that case.
Also: the capacity guard's ceiling search could two-cycle and return
whichever value the iteration budget's parity landed on, one credential
above the self-consistent ceiling — failing open in the direction it
exists to prevent. It now iterates to a real fixpoint and takes the lower
value on a cycle.
Verified: native 2860/2860, the variant-config guard over all 38
environments, and every one of the eight boards builds — including
m5core_ink, which had never been built directly before.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Add vault menu tests, fix case-insensitive icon classification, UI capture infrastructure
- Add test_vault_menu native tests (23 tests): menu selection cycling,
scroll window logic, credential icon classification, layout verification
- Fix drawCredentialIcon() case-sensitivity bug: Gmail/GITHUB/Wifi now
correctly match their icons using containsCI() with strncasecmp
- Add Gmail as Google icon alias in credential icon classification
- Add verifyIntegrity parameter to VaultStore::loadCredential() to skip
HMAC verification during menu listing (O(n) vs O(n*PBKDF2))
- Add serial debug commands: SCREENSHOT, ADDCRED, GOTO, WIPEVAULT, PIN
- Add PinEntryUI timing arc implementation with shake-to-clear
- Add VaultStore encrypted credential storage with AES-256-CBC + HMAC
- Add BLE HID multi-host support with keyboard layout system
- Add WiFi admin portal with credential management API
- Add TOTP generator and audit logging subsystems
- Add native test suites: crypto, bruteforce, TOTP, keyboard layouts,
shake logic, audit logic, vault JSON, password generator, holdbutton
- Add on-device test suites: vault_store, bruteforce_device, audit_device
- Add HAL abstractions: PowerManager, AudioFeedback, ShakeDetector, IMU
- Add FSM state handlers: PinState, VaultState, BleHidState, AdminState
- Add CI workflow, pre-commit config, developer documentation
- Add scripts: serial_screenshot.py, serial_button_test.py
- Restructure project: move prompts to .github/prompts/, add agents
- Update platformio.ini with native test environment and build filters
- Remove obsolete root-level prompt files and old docs
Right-size the flash layout and write the index once per import
Two capacity items that were blocking the raised per-variant caps.
The partition tables gave the app far more room than the largest image
needs while starving the vault: the 8 MB boards now run 2 x 0x2C0000 app
slots with a 0x260000 LittleFS, the 4 MB boards a 0x290000 app with
0x160000 of filesystem, and the 16 MB tables keep their geometry. The
loaded 8 MB build sits at 78 % of its app slot and the 4 MB ones near
70 %, so the headroom is real rather than asserted. Two rationales that
had gone stale — a ">1500 credentials" claim and an Arduino/ESP-TEE
headroom note — are gone.
Changing the layout means a device must be fully re-flashed and its vault
repopulated, which is the standing pre-release policy; the flashing and
variant docs now say so where someone upgrading would look.
The bulk import used to rewrite index.bin for every row, and each rewrite
materialized the whole capacity-scaled working set: at 500 credentials
that is a flash rewrite and ~89 KB of transient per credential. A batch
guard now defers the rows and rebuilds the index once at the end.
Deferral is only safe if a half-finished batch can never be mistaken for
a finished one, and review found three ways it could be:
- A transient key-derivation failure returned without closing the batch.
Every later index mutation then reported success while writing nothing,
favorites and ordering survived only in RAM, and the portal's
index-repair button reported success while doing nothing — until a
reboot. The batch now closes before the write, so a failed write cannot
leave mutators deferring into a buffer nobody will flush, and the
key-derivation path aborts the batch explicitly.
- If the pre-batch unlink failed — lfs_remove needs a metadata block, so
it fails on a full filesystem — the old index survived and the batch
deferred every row into oblivion, leaving a MAC-valid, complete-looking
index missing every imported credential with no path back. The batch
now refuses to open and per-row writes stand: slower, not wrong.
- The batch depth was atomic to survive the vault worker's inline-
execution fallback but the snapshot pointer was not, leaving a
use-after-free window in exactly that case.
Also: the capacity guard's ceiling search could two-cycle and return
whichever value the iteration budget's parity landed on, one credential
above the self-consistent ceiling — failing open in the direction it
exists to prevent. It now iterates to a real fixpoint and takes the lower
value on a cycle.
Verified: native 2860/2860, the variant-config guard over all 38
environments, and every one of the eight boards builds — including
m5core_ink, which had never been built directly before.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r