chore: checkpoint before lint autofix

This commit is contained in:
Sebastien L
2026-02-12 04:44:17 +00:00
parent 41c3056184
commit df225c120c
14 changed files with 795 additions and 14 deletions

View File

@@ -1,14 +1,19 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Never",
"IndentWidth": 4,
"TabWidth": 4,
"ColumnLimit": 150,
"PointerAlignment": "Left",
"AlignAfterOpenBracket": "DontAlign",
"BreakBeforeBraces": "Attach",
"AllowShortIfStatementsOnASingleLine": true,
"AllowShortFunctionsOnASingleLine": "All",
"IndentCaseLabels": false,
"SpacesBeforeTrailingComments": 1
}
---
BasedOnStyle: LLVM
UseTab: Never
IndentWidth: 4
TabWidth: 4
ColumnLimit: 150
PointerAlignment: Right
ReferenceAlignment: Pointer
AlignAfterOpenBracket: DontAlign
BreakBeforeBraces: Attach
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortBlocksOnASingleLine: Always
IndentCaseLabels: false
SortIncludes: false
SpaceBeforeParens: Never
ReflowComments: false
SpacesBeforeTrailingComments: 1

16
.clang-format-ignore Normal file
View File

@@ -0,0 +1,16 @@
# Third-party/vendor code
components/codecs/inc/**
components/esp-dsp/**
components/esp_http_server/**
components/spotify/cspot/**
components/squeezelite/**
components/telnet/libtelnet/**
components/tjpgd/**
managed_components/**
# Generated web artifacts
components/wifi-manager/webapp/dist/**
components/wifi-manager/webapp/src/js/proto/**
# Build outputs
build/**

33
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,33 @@
## Summary
- What changed:
- Why it changed:
## Test Strategy Check (Required)
- [ ] I validated behavior at contract/module boundaries (not private implementation details).
- [ ] New tests are refactor-resilient (should survive internal reorganization with identical behavior).
- [ ] For bug fixes, I added at least one regression test at the highest stable boundary.
- [ ] I avoided brittle assertions on internals (private helper call order, private layout, incidental log text), unless behavior-critical.
## Validation Performed
- [ ] Host/unit tests
- [ ] Hardware/HIL tests (if applicable)
- [ ] Platform profile(s) validated: `i2s` / `muse` / `squeezeamp` / N/A
Commands and results:
```bash
# paste exact commands used
```
## Risk and Rollback
- Risk level: low / medium / high
- Potential regressions:
- Rollback plan:
## Related Docs
- [ ] `documentation/TESTING_CHARTER.md` reviewed
- [ ] `documentation/HARDWARE_TEST_MATRIX.md` reviewed (if platform impact)

32
.github/workflows/style-lint.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Style Lint
on:
push:
branches:
- "refactoring"
- "main"
- "master-cmake"
pull_request:
branches:
- "refactoring"
- "main"
- "master-cmake"
jobs:
clang-format:
name: clang-format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install clang-format
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-format
- name: Check style
run: |
build-scripts/lint_style.sh check

94
AGENTS.md Normal file
View File

@@ -0,0 +1,94 @@
# AGENTS.md
This file is the default startup brief for Codex sessions in this repository.
Keep it short, directive, and high-signal.
## Mission
- Preserve platform behavior and stability.
- Prefer small, reversible changes over broad rewrites.
- Keep behavior unchanged unless a request explicitly asks for behavior changes.
## Working Scope
- Primary source code: `components/`, `main/`, `test/`.
- Build and release scripts: `build-scripts/`, `docker/`, `.github/workflows/`.
- Documentation: `documentation/`.
## Engineering Priorities
1. Correctness and safety.
2. Backward-compatible behavior.
3. Maintainability and clarity.
4. Build/test reproducibility.
5. Performance and footprint optimization.
Optimize in this order unless the task explicitly changes priorities.
## Do Not Edit By Default
- Vendor/external code:
- `components/esp-dsp/`
- `components/spotify/cspot/`
- `components/telnet/libtelnet/`
- `managed_components/`
- Build outputs and generated artifacts:
- `build/`
- `components/wifi-manager/webapp/dist/`
Only modify these paths when the task explicitly requires it.
## Behavioral Invariants
- Treat OTA/recovery reliability as a product-level contract.
- Treat partition layout as a fixed contract unless explicitly requested otherwise.
- Do not introduce platform-specific compile-time branches when runtime/config solutions are sufficient.
- Avoid unrelated churn; keep patches tightly scoped to the request.
## Standard Workflow
1. Read the request and identify touched modules and contracts.
2. Inspect current code and existing patterns in those modules.
3. Implement the smallest coherent patch that preserves contracts.
4. Run targeted validation commands.
5. Report changes, validation evidence, and residual risks.
## Validation Commands
- Style check: `build-scripts/lint_style.sh check`
- Style normalize: `build-scripts/lint_style.sh format`
- Firmware build (example): `idf.py build`
- Recovery footprint (platform-specific): `build-scripts/build_recovery_size.sh <platform|defaults-file> [build-dir]`
Use targeted checks first; avoid full-matrix builds unless requested.
## Testing Rule (Grounding)
- Tests should validate behavior contracts and invariants, not implementation details.
- Prefer module-boundary and regression tests over private-internal assertions.
- Every bug fix should include a regression test at the highest stable boundary possible.
- Use:
- `documentation/TESTING_CHARTER.md`
- `documentation/CONTRACT_TEST_TEMPLATE.md`
- `documentation/HARDWARE_TEST_MATRIX.md`
## Style Baseline
- Use repository formatting/lint settings as the source of truth.
- Do not introduce style-only churn outside task scope unless requested.
- During refactors, normalization is acceptable when intentionally planned.
## Frontend Context (Required)
- For any UI/API work involving `components/wifi-manager/webapp/` or `components/wifi-manager/http_server_handlers.c`, read:
- `documentation/agents/frontend_requirements_context.md`
- Treat that file as requirements-first context and keep it current.
- When frontend payload, routes, or protobuf contract changes, run:
- `build-scripts/ui_footprint_snapshot.sh`
- Then update `documentation/agents/frontend_requirements_context.md` in the same change.
## Agent Docs Layout
- Keep this file concise and policy-focused.
- Put detailed playbooks under `documentation/agents/`.
- Add module-specific guidance in local `AGENTS.md` files when needed (closest file to changed code takes precedence).

View File

@@ -6,6 +6,7 @@ ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
clang-format \
curl \
git \
git-lfs \

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: build-scripts/lint_style.sh [check|format]
Commands:
check Verify C/C++ style with clang-format (default)
format Apply clang-format in place
EOF
}
MODE="${1:-check}"
if [[ "$MODE" != "check" && "$MODE" != "format" ]]; then
usage
exit 2
fi
if ! command -v clang-format >/dev/null 2>&1; then
echo "clang-format is required but was not found in PATH." >&2
exit 127
fi
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
declare -a PATHSPECS=(
"*.c"
"*.h"
"*.cc"
"*.cpp"
"*.hpp"
":(exclude)build/**"
":(exclude)managed_components/**"
":(exclude)components/codecs/inc/**"
":(exclude)components/esp-dsp/**"
":(exclude)components/esp_http_server/**"
":(exclude)components/spotify/cspot/**"
":(exclude)components/squeezelite/**"
":(exclude)components/telnet/libtelnet/**"
":(exclude)components/tjpgd/**"
":(exclude)components/wifi-manager/webapp/dist/**"
":(exclude)components/wifi-manager/webapp/src/js/proto/**"
)
mapfile -t FILES < <(git ls-files -- "${PATHSPECS[@]}" | grep -E '^(components|main|test)/.*\.(c|h|cc|cpp|hpp)$' || true)
if [[ ${#FILES[@]} -eq 0 ]]; then
echo "No C/C++ files matched lint scope."
exit 0
fi
if [[ "$MODE" == "format" ]]; then
printf '%s\0' "${FILES[@]}" | xargs -0 -r clang-format -i
echo "Formatted ${#FILES[@]} file(s)."
exit 0
fi
declare -a FAILING=()
for file in "${FILES[@]}"; do
if ! clang-format --dry-run --Werror "$file" >/dev/null 2>&1; then
FAILING+=("$file")
fi
done
if [[ ${#FAILING[@]} -gt 0 ]]; then
echo "Style check failed in ${#FAILING[@]} file(s)."
echo "First failing files:"
printf ' - %s\n' "${FAILING[@]:0:30}"
echo "Run: build-scripts/lint_style.sh format"
exit 1
fi
echo "Style check passed for ${#FILES[@]} file(s)."

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
DIST_DIR="$ROOT/components/wifi-manager/webapp/dist"
if [[ ! -d "$DIST_DIR" ]]; then
echo "dist directory not found: $DIST_DIR" >&2
echo "Build webapp first (e.g. npm run build in components/wifi-manager/webapp)." >&2
exit 1
fi
python - "$DIST_DIR" <<'PY'
import os
import sys
import glob
from datetime import datetime, timezone
dist = sys.argv[1]
def rel(p):
return os.path.relpath(p, start=os.getcwd())
all_files = [p for p in glob.glob(os.path.join(dist, "**", "*"), recursive=True) if os.path.isfile(p)]
ship_files = [p for p in all_files if p.endswith(".gz") or p.endswith(".png")]
all_files_sorted = sorted([(os.path.getsize(p), p) for p in all_files], reverse=True)
ship_sorted = sorted([(os.path.getsize(p), p) for p in ship_files], reverse=True)
print(f"Snapshot UTC: {datetime.now(timezone.utc).isoformat(timespec='seconds')}")
print(f"Dist dir: {rel(dist)}")
print("")
print(f"All dist files: {len(all_files)}")
print(f"All dist bytes: {sum(s for s, _ in all_files_sorted)}")
print(f"Shipped files (*.gz + *.png): {len(ship_sorted)}")
print(f"Shipped bytes: {sum(s for s, _ in ship_sorted)}")
print("")
print("Top 15 dist files:")
for size, path in all_files_sorted[:15]:
print(f"{size:9d} {rel(path)}")
print("")
print("Shipped files:")
for size, path in ship_sorted:
print(f"{size:9d} {rel(path)}")
PY

View File

@@ -0,0 +1,21 @@
# AGENTS.md (Web UI)
Local guidance for work under `components/wifi-manager/webapp/`.
## Required Context
- Read `documentation/agents/frontend_requirements_context.md` before making UI/API changes.
- Treat that document as the active requirements context (not just historical notes).
## Scope Rules
- Prioritize behavior and contract clarity over framework churn.
- Do not edit `dist/` files manually.
- Keep generated protobuf artifacts and API usage aligned with the active request/response contract.
## Maintenance Rules
- If routes, payload contracts, or shipped web asset sizes change:
1. Run `build-scripts/ui_footprint_snapshot.sh`.
2. Update `documentation/agents/frontend_requirements_context.md`.
3. Add/update the decision log entry in that file.

View File

@@ -0,0 +1,44 @@
# Contract Test Template
Use this template when adding/refactoring tests to ensure they are refactor-resilient.
## Metadata
- Test ID:
- Module/Boundary:
- Priority (`P0`/`P1`/`P2`):
- Platform scope (`host`, `all`, `i2s`, `muse`, `squeezeamp`, etc.):
## Contract
Describe what externally visible behavior must remain true.
## Invariant(s)
List invariant properties this test protects.
## Inputs/Preconditions
- Required setup:
- Input variants:
- Fault conditions (if any):
## Expected Behavior
- Success conditions:
- Error conditions:
- Bounds/time/resource expectations:
## Non-goals
State what this test intentionally does not verify (to avoid implementation coupling).
## Refactor Resilience Check
- Would this still pass if internals were reorganized but behavior unchanged? (`yes`/`no`)
- If `no`, explain why coupling is unavoidable.
## Regression Linkage
- Related issue/bug (if regression test):
- Why this boundary was chosen:

View File

@@ -0,0 +1,130 @@
# Hardware Test Matrix
Date: 2026-02-12
Scope: Platform-specific stability and release validation for `squeezelite-esp32`.
## Purpose
Define a practical hardware-focused test strategy that complements host/unit tests and acknowledges embedded uncertainty under real-world load.
This matrix is designed to:
- maximize platform stability
- prioritize high-impact failures first
- provide clear release gates per hardware profile
Use together with:
- `documentation/TESTING_CHARTER.md`
- `documentation/CONTRACT_TEST_TEMPLATE.md`
## Platforms
Mapped to build profiles used in this repo:
- `i2s` -> `build-scripts/I2S-4MFlash-sdkconfig.defaults`
- `muse` -> `build-scripts/Muse-sdkconfig.defaults`
- `squeezeamp` -> `build-scripts/SqueezeAmp-sdkconfig.defaults`
## Priority Levels
- `P0`: release blocking, must pass
- `P1`: strong confidence tests, expected to pass before release
- `P2`: extended coverage/soak, required for nightly and milestone validation
## Execution Tiers
- PR smoke: fast subset (`P0` only, selected `P1`)
- Nightly: full `P0 + P1` and selected `P2`
- Pre-release: full matrix, all `P0/P1`, mandatory `P2` soak and fault-injection
## Core Hardware Matrix
| ID | Area | Test Path | Priority | Platforms | Pass Criteria |
|---|---|---|---|---|---|
| HW-BOOT-001 | Boot | Cold boot to operational state | P0 | all | boots successfully within target time budget; no panic/reset loop |
| HW-BOOT-002 | Boot | Warm reboot (`esp_restart`) loop x50 | P0 | all | no stuck boot, no boot partition confusion, stable counters |
| HW-BOOT-003 | Boot identity | Platform profile and key GPIO map sanity | P0 | all | expected `FW_PLATFORM_NAME`, expected peripheral init paths |
| HW-STOR-001 | Storage/NVS | NVS read/write/reset cycle | P0 | all | persisted values survive reboot, reset restores defaults |
| HW-STOR-002 | Storage/NVS | Corrupt/partial NVS handling | P1 | all | graceful recovery path, no crash, defaults restored or fallback used |
| HW-STOR-003 | SPIFFS | SPIFFS mount + defaults file read | P0 | all | mount succeeds, required files loaded, clear error if missing |
| HW-NET-001 | Wi-Fi | AP connect + DHCP + DNS | P0 | all | connected state reached, IP assigned, DNS query success |
| HW-NET-002 | Wi-Fi resilience | AP loss/recovery reconnect | P0 | all | reconnect within SLA, no task deadlock or watchdog |
| HW-NET-003 | Service discovery | mDNS announce/discover | P1 | all | service visible on LAN; no crash if mDNS unavailable |
| HW-NET-004 | Ethernet | Link up/down + DHCP + traffic | P1 | ethernet-capable | state transitions handled cleanly; no memory growth trend |
| HW-AUD-001 | Audio output | Start/stop playback, no signal path errors | P0 | all | successful init and playback lifecycle, no panic |
| HW-AUD-002 | Audio behavior | Format/rate transitions during playback | P1 | all | transitions without hard fault; bounded glitch behavior |
| HW-AUD-003 | Audio resilience | Underrun/rebuffer recovery | P1 | all | playback recovers, no runaway CPU or deadlock |
| HW-AUD-004 | Controls | Volume/mute/jack/speaker path checks | P1 | platform-specific | control actions reflected correctly in output state |
| HW-UI-001 | Inputs | Button/rotary/IR event mapping | P1 | platform-specific | expected commands dispatched; no ghost/repeat storm |
| HW-UI-002 | Display | Display init + update loop | P1 | display-capable | stable render/update path, no crash under repeated updates |
| HW-PWR-001 | Battery/charger | Battery telemetry and status logic | P1 | battery-capable | values in expected range; no invalid-state loop |
| HW-BT-001 | Bluetooth | Pair/connect/disconnect cycles | P1 | bt-enabled | stable lifecycle across repeated cycles |
| HW-BT-002 | Bluetooth resilience | BT stack restart/recovery | P2 | bt-enabled | stack recovers without reboot or hard fault |
| HW-OTA-001 | OTA | Happy-path OTA update | P0 | all | new image boots and reports expected version |
| HW-OTA-002 | OTA failure | Interrupted OTA (network/power cut) | P0 | all | safe fallback/rollback path works; device recoverable |
| HW-OTA-003 | Recovery partition | Recovery boot and exit path | P0 | all | recovery mode entry/exit consistent and deterministic |
| HW-PWRF-001 | Power fault | Brownout/power-cut during write/OTA | P0 | all | no bricking; deterministic recovery behavior |
| HW-SOAK-001 | Soak | 12h playback + periodic reconnect | P2 | all | no crash/reset; memory trend within threshold |
| HW-SOAK-002 | Soak | 24h mixed load (stream/control/network churn) | P2 | all | no regressions in responsiveness and stability |
## Platform-Specific Focus
## `i2s` (generic baseline)
- Emphasize:
- audio path stability under varied sample rates
- generic GPIO/peripheral defaults
- OTA + recovery baseline behavior
- Required:
- all `P0`
- `HW-AUD-002`, `HW-AUD-003`, `HW-SOAK-001`
## `muse` (portable/battery-centric)
- Emphasize:
- battery telemetry, charger events, low-power edge conditions
- UI/display responsiveness under battery/network churn
- Wi-Fi reconnect after sleep-like or low-power transitions
- Required:
- all `P0`
- `HW-PWR-001`, `HW-UI-001`, `HW-UI-002`, `HW-SOAK-001`
## `squeezeamp` (amp/control-centric)
- Emphasize:
- amplifier-related controls, output routing, jack/speaker transitions
- thermal/long-play stability proxies (extended playback)
- network resilience during active playback
- Required:
- all `P0`
- `HW-AUD-004`, `HW-NET-002`, `HW-SOAK-001`, `HW-SOAK-002`
## Release Gates
A release is eligible only if:
- every target platform passes all `P0` tests
- no unresolved regression in previously passing `P1` tests
- at least one `P2` soak run per platform completed within release window
- OTA/recovery tests (`HW-OTA-001/002/003`) pass on all platforms
## Observability Requirements During Tests
Collect at minimum:
- reset reason, panic reason, boot count, recovery boot count
- heap free/minimum and largest block trend
- task stack high-water marks for critical tasks
- network reconnect counters and durations
- OTA stage/result events
A test can be marked "pass with warning" only for non-`P0` tests and only with a filed follow-up issue and owner.
## Failure Triage Rules
- `P0` fail: immediate release block
- repeated `P1` fail on same platform: treat as release risk, escalate to block unless waived
- any soak fail with crash/reset: open defect with logs and reproduction steps before next release candidate
## Suggested First Implementation Wave
1. Automate all `P0` checks for each platform profile.
2. Add nightly `P1` network/audio/UI subset.
3. Add 12h soak (`HW-SOAK-001`) with telemetry capture and trend thresholds.
4. Expand to full pre-release matrix once stable signal quality is established.

View File

@@ -0,0 +1,64 @@
# Testing Charter
Date: 2026-02-12
Scope: `squeezelite-esp32` test design and review policy.
## Objective
Keep tests stable across refactors by validating behavior contracts and invariants, not implementation details.
## Grounding Rule
Tests must verify behavior that is expected to remain true after refactoring.
Tests should be rejected in review if they mainly assert internals that can change without changing user-visible or system-visible behavior.
## Contract-First Principles
1. Test module boundaries
- public function behavior
- state machine transitions
- error semantics
- resource/timing bounds where relevant
2. Prefer invariants over internals
- "must never crash on malformed input"
- "illegal state transition is rejected"
- "retry is bounded"
- "idempotent operation remains safe when repeated"
3. Avoid implementation coupling
- do not assert private helper call order unless behavior-critical
- do not assert exact logs/strings unless contractually required
- do not assert private data layout
4. Fixes require regression tests
- every production bug fix should add a regression test at the highest stable boundary that catches it
5. Embedded + host split
- host-native tests for deterministic logic
- hardware/HIL tests for timing, drivers, memory layout, OTA/recovery, power fault behavior
## Review Gate
Reviewers should ask:
1. What contract does this test protect?
2. Would this test still pass after a clean internal refactor with identical behavior?
3. Does this test provide failure signal that matters to users/platform stability?
If answers are weak, request a contract-level test instead.
## Minimum PR Expectations
- New behavior: at least one contract-level test
- Bug fix: at least one regression test
- Refactor-only PR: existing contract tests still pass, no new implementation-coupled tests added
## Mapping to Hardware Matrix
Use this charter with:
- `documentation/HARDWARE_TEST_MATRIX.md`
The charter governs *how* tests are written.
The matrix governs *where/what* is validated per hardware platform.

View File

@@ -0,0 +1,20 @@
# Agent Documentation
Store long-form guidance for coding agents here.
## Suggested Files
- `documentation/agents/architecture.md`: subsystem boundaries, data flow, invariants.
- `documentation/agents/build-and-test.md`: canonical commands, fast checks, CI mapping.
- `documentation/agents/style-and-lint.md`: formatting policy, lint severity, suppression rules.
- `documentation/agents/refactor-playbook.md`: safe refactor steps, rollout strategy, risk controls.
- `documentation/agents/frontend_requirements_context.md`: requirements-first UI context, size budgets, and migration constraints.
- `documentation/agents/module-notes/<module>.md`: module-level constraints and edge cases.
## Writing Rules
- Keep files operational and concrete.
- Prefer checklists and explicit commands.
- Record known pitfalls and non-obvious invariants.
- Link back from `AGENTS.md` only to docs that are actively maintained.
- When frontend payload/routes/contracts change, refresh the snapshot with `build-scripts/ui_footprint_snapshot.sh` and update `frontend_requirements_context.md`.

View File

@@ -0,0 +1,201 @@
# Frontend Requirements Context (Living)
Status: Active
Last reviewed: 2026-02-12
Review scope: `components/wifi-manager/webapp`, `components/wifi-manager/http_server_handlers.c`, `components/wifi-manager/wifi_manager_http_server.c`, `spiffs_src/CMakeLists.txt`
## Purpose
This document captures reverse-engineered **product requirements** for the embedded web UI, with measurable constraints.
It is intentionally not an implementation walkthrough.
Use this as session handoff context for UI refactoring and footprint control.
## Snapshot Findings (Current State)
### 1. What actually ships to firmware
SPIFFS packaging currently copies only:
- `components/wifi-manager/webapp/dist/*.gz`
- `components/wifi-manager/webapp/dist/*.png`
Evidence: `spiffs_src/CMakeLists.txt`.
Current shipped web payload:
- Total shipped bytes: `219741` (`~214.6 KiB`, `~0.21 MiB`)
- Largest asset: `dist/js/node_vendors.bundle.js.gz` (`166729` bytes)
### 2. What looks large but is not currently shipped
- `dist/` total is about `4.5M`, dominated by source maps (`*.map`).
- Source maps are large in repo artifacts, but not copied to SPIFFS by current packaging.
### 3. Major technical debt signal
- UI source references many legacy endpoints (`/status.zzz`, `/messages.zzz`, `/commands.zzz`, etc.).
- Active server registration in `wifi_manager_http_server.c` is centered on `/data.bin` + static handlers; many legacy handlers are commented out.
This indicates API/contract drift and unclear migration state.
### 4. Modularity and proto usage signal
- Main UI logic is largely monolithic in `src/js/custom.ts` (~2.5k LOC).
- `index.ts` imports generated protobuf bundle (`configuration_pb.js`) up front.
- `dist/js/index.bundle.js` includes many generated proto message definitions, not only request/response minimum set.
Implication: upfront JS parse/execute and bundle weight are likely higher than needed.
## Reverse-Engineered Product Requirements
These are the requirements we should preserve while refactoring.
### RQ-UI-001 Captive Portal Bootstrap
The UI must reliably open and render from AP/captive-portal entry points with no manual URL knowledge.
Acceptance:
- Landing page reachable from captive portal probes.
- First meaningful controls visible without requiring cloud services.
### RQ-UI-002 Network Onboarding and Recovery
Users must be able to scan APs, join/disconnect Wi-Fi, and understand current link mode (Wi-Fi vs Ethernet) with clear status.
Acceptance:
- Scan, connect, disconnect workflows remain available and understandable.
- Connection state transitions are visible and unambiguous.
### RQ-UI-003 Runtime Observability
Users must see operational status and logs needed for diagnosis (network, playback, OTA progress, errors).
Acceptance:
- Status view remains available in normal and recovery mode.
- Error and warning paths are visible in UI without dev tools.
### RQ-UI-004 Safe Configuration Editing
Users must be able to view/edit/apply configuration with guardrails (validation feedback, clear commit/apply behavior).
Acceptance:
- Config edit path supports both selective and global update actions.
- Validation errors are explicit before/after submit.
### RQ-UI-005 OTA Operations
Users must be able to trigger firmware update by URL or upload, with progress and failure feedback.
Acceptance:
- OTA action always yields deterministic status progression or explicit terminal error.
- Recovery path remains accessible if normal OTA path fails.
### RQ-UI-006 Advanced Control Access
Power-user controls (commands/NVS/diagnostics) must exist but should not degrade common-task UX.
Acceptance:
- Advanced functions remain accessible.
- Main tasks are not blocked by advanced UI complexity.
### RQ-UI-007 Backward Compatibility Envelope
The UI must support currently deployed firmware API variants during migration (legacy and protobuf-era endpoints), with explicit deprecation strategy.
Acceptance:
- Contract compatibility matrix is documented and tested.
- Legacy paths have clear sunset criteria.
### RQ-UI-008 Embedded Footprint Discipline
Web assets must stay within explicit firmware budget and be measurable in CI.
Acceptance:
- Shipping budget exists and is enforced.
- Size regressions fail checks unless explicitly approved.
## Non-Functional Budgets (Proposed)
These are targets, not yet enforced:
- `NB-001` Shipped web payload (`*.gz` + `*.png` in `dist/`) <= `180 KiB` target, `220 KiB` hard cap.
- `NB-002` Main JS gzip <= `120 KiB` target.
- `NB-003` Vendor JS gzip <= `120 KiB` target.
- `NB-004` No runtime dependency on external CDNs for critical UI function.
- `NB-005` One canonical API transport for new features (`/data.bin` protobuf), with compatibility shim for legacy endpoints during migration only.
## UX + Footprint Improvement Directions
Prioritized by impact/risk.
### P1 Contract Unification (High impact, medium risk)
- Define one frontend API client contract around protobuf request/response.
- Treat legacy `.zzz` paths as compatibility layer, not primary path.
- Add explicit compatibility table (firmware version -> supported endpoints).
### P2 Modularization by Capability (High impact, medium risk)
- Split monolithic UI logic into capability modules:
- onboarding
- status/logs
- config editor
- OTA
- advanced
- Lazy-load non-critical modules (advanced, release browser, heavy editors).
Expected result: lower startup JS cost and clearer ownership boundaries.
### P3 Proto Surface Minimization (High impact, medium risk)
- Stop importing broad generated config surface on initial load.
- Create thin protobuf DTO layer for startup-critical flows.
- Load heavy schema modules only when entering related screens.
### P4 UI Responsiveness and Clarity (Medium impact, low risk)
- Replace mixed polling patterns with a single scheduler and explicit backoff policy.
- Standardize in-progress/error/success state rendering across all actions.
- Reduce ambiguous icon-only cues with text fallback in constrained/offline contexts.
### P5 Build Output Hygiene (Medium impact, low risk)
- Keep generating sourcemaps for dev/debug if needed, but separate from shipping artifact workflow.
- Enforce shipped-size budget on what SPIFFS actually receives, not raw `dist/`.
## Living Maintenance Protocol
This file must be updated when any of the following changes:
- web routes/endpoints
- protobuf request/response schema used by UI
- shipped asset packaging rules
- significant UI feature flow
Required update steps:
1. Run `build-scripts/ui_footprint_snapshot.sh`.
2. Update Snapshot Findings and budget deltas in this file.
3. Update compatibility notes if API contract changed.
4. Add an entry to Decision Log.
## Decision Log
| Date | Decision | Why | Follow-up |
|------|----------|-----|-----------|
| 2026-02-12 | Treat this file as requirements-first context, not implementation notes | Reduce drift and speed up session handoffs | Add CI size gate and API contract checks |
## Open Questions
- Are legacy `.zzz` endpoints still required for deployed firmware versions, and for which versions exactly?
- Is the intended long-term model pure protobuf over `/data.bin` for all dynamic data?
- What startup latency target is acceptable on AP-mode captive portal connections?