Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r
Fix twelve StickS3 defects and give every brand tile a square mark
A hands-on QA pass over the StickS3 -- every step a real button gesture through
the production input path, every fix verified on the panel, not in the code.
Firmware defects found and fixed:
- Footer labels overlapped in every language but English. A base cell's label
was drawn centred with no measurement, so "A MANTENER" overprinted "B SALTAR"
and clipped at the bezel. Labels now clip to their own cell.
- Overlays swallowed the gesture that opened them. The settings tree activates
on a hold's RELEASE, leaving the PRESS edge latched; the overlay then consumed
it. Info panels closed the instant they opened and the brightness stepper took
a phantom nudge that wrapped it to the floor.
- Closing an overlay repainted only the list body, leaving the overlay's own
header and footer framing the tree.
- The configured brightness was never applied. disp_br had a writer and no
reader, five sites forced the ceiling on every wake, and the settings row read
the panel's live level instead of the stored one -- three different answers.
- The header band (clock chip, BLE link, battery) only painted on a full
repaint, so an NTP sync left its warning up until something else forced one.
- Debug stay-awake sentinels (UINT32_MAX) were persisted to NVS, and NVS
survives a re-flash: a debug session could leave a release image that never
slept.
- commitStepperValue's `default:` fell through into PinSweep, so any misrouted
option silently rewrote the PIN-arc sweep.
- LittleFsGuard's one-argument constructor read mounted_ from inside its own
mem-initialiser. Whichever way the stack garbage fell, the guard could report
success without mounting, and the caller measured an unmounted filesystem.
- OPTGET/OPTLIST read every key from the "kleidos" namespace, so lang
(kleidos-ob) and theme (ui) answered "unset" for keys that were set.
- The info panel drew each line at a fixed x with no measurement: the MAC's last
octets fell off the right edge. Lines now wrap with an indented continuation.
- KLEIDOS_VARIANT was defined on two boards of eight, so the rest reported
"Variant: (unknown)"; the storage line read 0/0 KB because it queried LittleFS
unmounted.
- Four documentation claims contradicted the code (PIN submission, the lockout
threshold, list wrap-around).
Owner decisions implemented: shorter footer verbs; a clock-sync action on the
TOTP page when a network is saved; no lock face during first-run PIN creation;
DISP? reports the brightness before perturbing it; wrapped info panels; and the
three-action popup preview refuses on a two-button board.
Brand tiles:
98 of 200 logo sources were not square, and all three rasterizer paths forced
size x size with the aspect ignored, so half the catalogue rendered squashed.
Fixed at three levels: 106 brands re-pointed at their icon-only mark (the Amazon
smile, the Stripe S, the Netflix N) with 23 regaining real colour; the pipeline
now scales to fit and centres, never stretches; and verify_brands.py fails any
source outside the aspect band unless it is one of the fifteen brands with no
icon-only mark at all. Four upstreams are newly sanctioned -- two of them
CC BY 4.0, so docs/brand-logos.md now carries the attribution they oblige.
Dashlane and Cloudinary have no better artwork anywhere, so they take a plate
treatment: the brand colour behind a light glyph, with the plate luma held in
the window where both contrasts clear 3:1.
Also removes KLEIDOS_BRANDS_CACHE_SLOTS, declared in all eight variants and
consumed by nothing, and corrects two stale figures in docs/brand-logos.md.
Adds qa/tools/human_qa.py, the gesture-level harness this pass was driven with.
Verified: 2555/2555 native tests, 41/43 fleet environments (the two native envs
hit the known Unity install flake under `pio run`), cppcheck 0, clang-tidy 0,
brand manifest 200/200, pre-commit green, BLE hardware suite 11 passed.
Claude-Session: https://claude.ai/code/session_01Q2J5gQSFMTDLVzPUYog51r