Two effect IDs don't mean what the code says they mean #132

Closed
opened 2026-09-05 00:52:15 +00:00 by claude-bot · 1 comment
Contributor

Found while working on #14, by checking the codebase's hardcoded effect IDs against GET /json/eff on the real 16.0.0 controller rather than against the comments next to them.

1. The aurora scheme pushes Tetrix, not Aurora

backend/app/services/aurora.py sets "fx": 44 with the comment:

Effect 44 is Aurora in stock WLED, which is exactly the thing being depicted.

Effect 44 is Tetrix. Aurora is 38. I wrote that comment in #20 and it was wrong — I asserted the ID rather than checking it, which is exactly the failure mode this project keeps running into. The aurora feature is env-gated and off by default, so nobody has seen it, but anyone who enabled AURORA_ENABLED would get a falling-blocks animation during a geomagnetic storm.

2. The AI's fallback effect catalogue is mostly mislabeled

prompt_builder.build_prompt uses a live catalogue from the controller when one is available, and this hardcoded list when it isn't:

Sent to the model Actually is Correct ID for that name
{id: 0, name: "Solid"} Solid
{id: 9, name: "Chase"} Rainbow 28
{id: 65, name: "Colorful"} Palette 34
{id: 66, name: "Traffic"} Fire 2012 35
{id: 42, name: "Fireworks"} Fireworks
{id: 57, name: "Fire 2012"} Lightning 66
{id: 108, name: "Twinkle"} Sine 17

Five of seven are wrong. The model picks an effect by name — "Twinkle suits a winter scene" — and gets the ID beside it, so a scheme generated without a controller connected asks for Sine when it meant Twinkle. This is the path a new user hits before wiring hardware, and #63's demo mode will make it the default experience.

schemes/builtin.yaml was audited at the same time and is clean — all eight IDs it uses (0, 2, 9, 21, 45, 57, 65, 66) are real effects and plausible choices for their schemes.

Fix

  • aurora.py: 44 → 38
  • prompt_builder: correct the five wrong pairs
  • A guard test asserting every hardcoded ID→name pair matches tests/fixtures/wled/16.0.0/eff.json — the catalogue captured verbatim from the real device in #62. Without it these silently rot again, since nothing else ever reads an effect name.

Why this keeps happening

Three times now a fact about WLED has been asserted from memory and been wrong: leds.segs (#62), the transition-field range (#52), and now these. The captured fixtures exist precisely so the answer can be checked instead of recalled — the guard test above is the piece that makes them load-bearing rather than decorative.

Found while working on #14, by checking the codebase's hardcoded effect IDs against `GET /json/eff` on the real 16.0.0 controller rather than against the comments next to them. ## 1. The aurora scheme pushes Tetrix, not Aurora `backend/app/services/aurora.py` sets `"fx": 44` with the comment: > Effect 44 is Aurora in stock WLED, which is exactly the thing being depicted. **Effect 44 is `Tetrix`.** `Aurora` is **38**. I wrote that comment in #20 and it was wrong — I asserted the ID rather than checking it, which is exactly the failure mode this project keeps running into. The aurora feature is env-gated and off by default, so nobody has seen it, but anyone who enabled `AURORA_ENABLED` would get a falling-blocks animation during a geomagnetic storm. ## 2. The AI's fallback effect catalogue is mostly mislabeled `prompt_builder.build_prompt` uses a live catalogue from the controller when one is available, and this hardcoded list when it isn't: | Sent to the model | Actually is | Correct ID for that name | |---|---|---| | `{id: 0, name: "Solid"}` | Solid ✅ | — | | `{id: 9, name: "Chase"}` | **Rainbow** | 28 | | `{id: 65, name: "Colorful"}` | **Palette** | 34 | | `{id: 66, name: "Traffic"}` | **Fire 2012** | 35 | | `{id: 42, name: "Fireworks"}` | Fireworks ✅ | — | | `{id: 57, name: "Fire 2012"}` | **Lightning** | 66 | | `{id: 108, name: "Twinkle"}` | **Sine** | 17 | Five of seven are wrong. The model picks an effect *by name* — "Twinkle suits a winter scene" — and gets the ID beside it, so a scheme generated without a controller connected asks for Sine when it meant Twinkle. This is the path a new user hits before wiring hardware, and #63's demo mode will make it the default experience. `schemes/builtin.yaml` was audited at the same time and is **clean** — all eight IDs it uses (0, 2, 9, 21, 45, 57, 65, 66) are real effects and plausible choices for their schemes. ## Fix - [ ] `aurora.py`: 44 → 38 - [ ] `prompt_builder`: correct the five wrong pairs - [ ] A guard test asserting every hardcoded ID→name pair matches `tests/fixtures/wled/16.0.0/eff.json` — the catalogue captured verbatim from the real device in #62. Without it these silently rot again, since nothing else ever reads an effect name. ## Why this keeps happening Three times now a fact about WLED has been asserted from memory and been wrong: `leds.segs` (#62), the transition-field range (#52), and now these. The captured fixtures exist precisely so the answer can be checked instead of recalled — the guard test above is the piece that makes them load-bearing rather than decorative.
Author
Contributor

Fixed in #133 (squash-merged to main). All three checkboxes done.

Both bugs were reintroduced to confirm the guard fails with the right diagnosis before the fixes were kept:

assert 'Tetrix' == 'Aurora'
assert ["id 108 is labelled 'Twinkle' but is 'Sine'"] == []

The fallback catalogue went from seven mostly-wrong pairs to ten verified ones, chosen to span genuinely different looks so the model has something to choose between rather than a row of near-identical effects.

The guard is the part worth keeping. tests/fixtures/wled/16.0.0/eff.json already existed — captured verbatim in #62 — but nothing read it, so it was decorative. Now it's the only thing standing between a remembered integer and a scheme that renders as something nobody chose.

The builtin.yaml audit is pinned too: a range check, plus a check that the walker actually finds IDs, so it can't quietly pass on an empty set.

Verified: 10 new tests, ruff/mypy clean, 759 backend tests, CI green.

Follow-on: #14 extends the same guard to the frontend simulator, which had the same class of error — it keyed its animations on 57, 65, 66 and 9 believing them to be a comet, a wave, traffic lanes and a chase.

Fixed in #133 (squash-merged to `main`). All three checkboxes done. Both bugs were **reintroduced to confirm the guard fails with the right diagnosis** before the fixes were kept: ``` assert 'Tetrix' == 'Aurora' assert ["id 108 is labelled 'Twinkle' but is 'Sine'"] == [] ``` The fallback catalogue went from seven mostly-wrong pairs to ten verified ones, chosen to span genuinely different looks so the model has something to choose between rather than a row of near-identical effects. The guard is the part worth keeping. `tests/fixtures/wled/16.0.0/eff.json` already existed — captured verbatim in #62 — but nothing read it, so it was decorative. Now it's the only thing standing between a remembered integer and a scheme that renders as something nobody chose. The `builtin.yaml` audit is pinned too: a range check, plus a check that the walker actually finds IDs, so it can't quietly pass on an empty set. Verified: 10 new tests, `ruff`/`mypy` clean, 759 backend tests, CI green. **Follow-on:** #14 extends the same guard to the frontend simulator, which had the *same* class of error — it keyed its animations on 57, 65, 66 and 9 believing them to be a comet, a wave, traffic lanes and a chase.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#132
No description provided.