mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 04:38:55 +00:00
Add docs/AUDIT*.md covering competitive landscape (easy-floorplan 11→430★), implementation quality, functional integrity, and P0–P3 recommendations. Refresh PRODUCT.md competitor claim and point STATUS watchlist at the pack. Co-authored-by: Matysh <Matysh@users.noreply.github.com>
133 lines
6.7 KiB
Markdown
133 lines
6.7 KiB
Markdown
# Audit — implementation quality
|
||
|
||
> Snapshot: **2026-08-05**, product **v1.58.0**.
|
||
> Severity: **critical / major / minor / nit**. No critical RCE found under
|
||
> normal HA session auth.
|
||
|
||
## 1. Architecture snapshot
|
||
|
||
```
|
||
src/ Lit 3 Lovelace cards (houseplan + space-card)
|
||
houseplan-card.ts ~8691 LOC — orchestration god-object
|
||
logic.ts / devices.ts / … extracted pure modules (good)
|
||
styles.ts ~2223 LOC CSS-in-JS
|
||
custom_components/houseplan/ HA integration (storage, WS, HTTP, trails)
|
||
dist/ + frontend/ committed bundle (CI byte-compares)
|
||
demo/ Playwright harness + smoke_*.mjs
|
||
tests_backend/ pure + HA-harness pytest
|
||
```
|
||
|
||
Bundle: **≈449 KB** raw / **≈128 KB** gzip — acceptable for a full editor+viewer.
|
||
|
||
## 2. What is strong
|
||
|
||
### Backend
|
||
|
||
- Single write-auth helper (`auth.may_write`) with **fail-closed** when entry
|
||
missing (audit B2 lesson documented in the module).
|
||
- Voluptuous validation with **cross-language option-list tests** that parse
|
||
`DISPLAY_MODES` / `TAP_ACTIONS` from `logic.ts` — rare and valuable.
|
||
- Config CAS via `expected_rev`; plan COW + quotas; never-delete-on-inference
|
||
file policy (SCOPE).
|
||
- Geometry migration with durable `geom_pending` intent (HP-1490 class).
|
||
- Content HTTP: `requires_auth`, signed URLs for `<image href>`, SVG CSP sandbox.
|
||
- Diagnostics / repairs / system_health present; `quality_scale.yaml` mostly
|
||
**honest** (`test-coverage`, `strict-typing`, docs todos marked todo).
|
||
|
||
### Frontend (extracted brain)
|
||
|
||
- Tap security model in `resolveTapAction` — locks/alarms never toggle from the
|
||
plan; cover garage/door/gate guarded; `run` limited to automation/script/scene.
|
||
- Pure geometry / devices / sun / vacuum / resize / align-grid / signing modules
|
||
with solid `node:test` coverage (~270 tests).
|
||
- Shared `config-store` + space-geometry/render for `houseplan-space-card`.
|
||
- Optimistic UI without rollback is **documented intentional** (ARCHITECTURE).
|
||
|
||
### Process
|
||
|
||
- Four test layers + smoke policy (“`[manual]` must have a failing auto check”).
|
||
- Dual CHANGELOG (en/ru), SCOPE guard rail, CONTRIBUTING five-minute loop.
|
||
- CI: hacs + hassfest + frontend + backend + smoke job.
|
||
|
||
## 3. Findings (ranked)
|
||
|
||
| # | Sev | Finding | Where |
|
||
|---|---|---|---|
|
||
| 1 | **major** | God-object card: ~8691 LOC, ~359 methods, ~125 private fields; `render()` ~382 lines. Smokes-only coverage for orchestration. | `src/houseplan-card.ts` |
|
||
| 2 | **major** | `strict: true` hollowed by `noImplicitAny: false`; ~200 `any` sites in the card alone; `hass: any` everywhere | `tsconfig.json`, card / devices / logic |
|
||
| 3 | **major** | Write-policy split: UI `_canEdit` is always admin-gated; API default `admin_only=False` lets **any** authenticated user write via WS/HTTP | `houseplan-card.ts` ≈L247–249, `auth.py` |
|
||
| 4 | **major** | `_canEdit` fails **open** when `hass.user` is missing (`is_admin !== false`) | card ≈L248 |
|
||
| 5 | **major** | `styles.ts` ~2223 LOC — second maintainability sink; untested | `src/styles.ts` |
|
||
| 6 | **minor** | `layout/update` has no `expected_rev` — multi-tablet last-writer-wins per point | `websocket_api.py` |
|
||
| 7 | **minor** | Marker `binding` is bare `str`; `ripple_color` not hex-matched; decor `w`/`h` allow negatives via `_NORM` | `validation.py` |
|
||
| 8 | **minor** | Card-level `tap_action` / `resolveTapAction` cardDefault is dead / ignored — confusing API surface | `types.ts`, click path |
|
||
| 9 | **minor** | Store `_async_migrate_func` is a no-op; real migrations live ad-hoc in setup | `store.py`, `__init__.py` |
|
||
| 10 | **nit** | `quality_scale.yaml` cites `test_config_flow.py`; file is `test_ha_config_flow.py` | quality_scale.yaml |
|
||
| 11 | **nit** | Duplicated `fireEvent` / `navigate` in card + space-card | both files |
|
||
| — | positive | Forbidden-domain tap model + confirm + cover guards | `logic.ts` |
|
||
| — | positive | Signed content + SVG sandbox | `http_api.py`, `signing.ts` |
|
||
|
||
**No critical** auth bypass of HA sessions found. Closest systemic issue is
|
||
finding #3 (API openness vs UI) under the default options.
|
||
|
||
## 4. TypeScript & tooling
|
||
|
||
| Item | State |
|
||
|---|---|
|
||
| `tsc --noEmit` in build | Yes — correct (rollup TS plugin can warn-and-ship) |
|
||
| ESLint / Prettier | **Absent** |
|
||
| `noImplicitAny` | **Off** |
|
||
| Frontend unit of the Lit class | **None** |
|
||
| mypy strict (backend) | quality_scale **todo** |
|
||
|
||
## 5. Test map
|
||
|
||
| Layer | What it proves | Gap |
|
||
|---|---|---|
|
||
| `npm test` (~270) | Pure logic, i18n parity, tap security, geometry | Not the card shell |
|
||
| `pytest` pure | validation.py without HA | — |
|
||
| HA-harness (py≥3.13) | setup, WS races, auth, upload, geometry repair | Coverage % unmeasured |
|
||
| `demo/smoke_*.mjs` (~100) | Real pointer/UI against fake hass | Pixel flakes (`smoke_opening_measure`); no Safari/Companion matrix |
|
||
|
||
Human checklist in TESTING.md last full self-run recorded at **v1.21.1** while
|
||
product is at **v1.58.0** — auto net grew; documented human pass did not.
|
||
|
||
## 6. Docs drift (quality of truth)
|
||
|
||
| Doc | Drift |
|
||
|---|---|
|
||
| `ARCHITECTURE.md` intro | Still mentions old `src/data/house.ts` / 1489×1053 era in places; square/infinite canvas sections newer |
|
||
| `UX-MODES.md` header | Still says “No code has been changed yet” — Phase 11 shipped |
|
||
| `PRODUCT.md` competitor table | **Stale** — easy-floorplan 11→430★ (see AUDIT-MARKET) |
|
||
| `STATUS.md` | Generally current as of 2026-08-04 |
|
||
|
||
Docs discipline is a project strength; these drifts are fixable and should be
|
||
treated as debt, not ignored.
|
||
|
||
## 7. Top 10 technical debt (actionable)
|
||
|
||
1. Split `houseplan-card.ts` into shell + Plan/Devices/Decor editors + dialogs.
|
||
2. Turn on `noImplicitAny` incrementally; type a thin `Hass` surface.
|
||
3. Align write policy: wire UI to `admin_only`, or default `admin_only=True`.
|
||
4. Fail closed on missing `hass.user`.
|
||
5. Add CAS / expected_rev to `layout/update` (or document single-writer assumption).
|
||
6. Split `styles.ts` by feature surface.
|
||
7. Tighten MARKER_SCHEMA (`binding`, colors, decor extents, space ids).
|
||
8. Unit-test orchestration hotspots (`_clickDevice`, write-chain conflicts, modes).
|
||
9. Real Store migrations or document setup-time migrations as the only path.
|
||
10. Remove or re-wire dead card-level `tap_action` / cardDefault.
|
||
|
||
Detailed priority and sequencing: [`AUDIT-RECOMMENDATIONS.md`](AUDIT-RECOMMENDATIONS.md).
|
||
|
||
## 8. Quality-scale honesty check
|
||
|
||
| Claim | Audit view |
|
||
|---|---|
|
||
| Bronze structural items `done` | Accurate |
|
||
| Silver unloading / owner `done` | Accurate |
|
||
| Gold diagnostics / repairs `done` | Accurate |
|
||
| `test-coverage` / `strict-typing` / docs examples `todo` | Accurate — keep them todo until measured |
|
||
| `config-flow-test-coverage` path typo | Nit — fix filename in yaml |
|
||
|
||
Overall: self-assessment is trustworthy; do not mark coverage done without a number.
|