docs(hygiene): свести дубли документов подсистем и снять устаревшее (#679)

Волна 2 эпика #674 — у каждого правила один дом, остальные места ссылаются.

DECOR-EDITOR.md ← BACKDROP.md + LIVE-TEXT.md: один документ с нумерованными
разделами (§3 подложка, §5 текст с живыми значениями), на которые теперь
указывают комментарии кода вместо несуществовавших «BACKDROP §2/§3»;
исправлено утверждение, что space-card не рисует декор (он рисует подложку
и картинки декора, но не фигуры, мебель и текст). LIGHT.md ← матрица
настроек света (перевод, тест назван явно: test/devices.test.mjs «issues
84/88»). DEVICE-PRESENTATION.md ← правила «что показывает маркер» из
FILTERING.md (порядок cover → light sources → device role, шторы,
медиаплееры); «в одном pull request» → «в одном коммите». CANVAS.md: §9.5
«Оптимизировать планы» → CONFIG-COMPATIBILITY.md, overlay и планарные грани
Walls → WALL-THICKNESS.md §10–11, таблицы «было/стало» сняты. TESTING-DEMO.md
→ demo/stand/README.md: карта демо-дома и «чего на стенде нет», ручной
чек-лист снят (ручной фазы в процессе нет). ISOMETRIC.md — только текущее;
история Stage 2/4 — docs/adr/570-isometric-stage4-visual-handoff.md.
SUN.md: удалённый контракт фона снят, правило бумаги — в текущем разделе.
UX-MODES.md: декор над заливками, а не «под комнатами»; «hidden isometric»;
follow-up из #3 — все выпущены. Шапки VACUUM, WARM-REMOUNT («Выровнять всё
по сетке» → «Оптимизировать планы»), WALL-THICKNESS, STYLING-HOOKS,
CONFIG-COMPATIBILITY (#33), PDF-EXPORT — без устаревших статусов и планов.
README EN/RU: абзац про пересъёмку скриншотов → CONTRIBUTING.md, RADAR и
PDF-EXPORT в списке документации, RU догнал EN (2.5D, повторное
использование загруженного изображения, STAIRS). Один список канонических
документов подсистем в AGENTS.md и промпте ревьюера (_process.yml).
WALL-THICKNESS.md ссылается на ADR 282.

Правки src/** и validation.py — только пути документов в комментариях.

Issue: #679
User-Visible: no
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
Claude
2026-09-27 18:07:37 +00:00
committed by claude[bot]
parent f989fbfe3d
commit 5a258f3128
48 changed files with 1229 additions and 1634 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
// Backdrop image (docs/BACKDROP.md) — two shots for the owner:
// Backdrop image (docs/DECOR-EDITOR.md §3) — two shots for the owner:
// backdrop_frame — the transform frame mid-gesture: dashed outline, four
// finger-sized corner handles, and the live "W × H" badge
// stating the picture's real size through cell_cm;
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* Backdrop image: move, uniform scale, and the new paper rule (docs/BACKDROP.md).
* Backdrop image: move, uniform scale, and the new paper rule (docs/DECOR-EDITOR.md §3).
*
* The whole contract, on the demo's f1 — which IS an image plan, so every
* assertion here lands on exactly the case that used to behave differently:
@@ -234,7 +234,7 @@ check('reset_button_gone_again', await q('.editor-secondary .bdreset'), 0);
// ---------- 5b) the SELECT tool still pans right over the picture ---------
// The body of the picture is most of the screen; claiming it outside its own
// tool would take away the one-finger pan (docs/BACKDROP.md §2), which is why
// tool would take away the one-finger pan (docs/DECOR-EDITOR.md §3.2), which is why
// moving is a tool. smoke_pan_any_zoom guards the same thing from the outside.
await tool('select');
await settle();
+1 -1
View File
@@ -211,7 +211,7 @@ checkAll(res);
// .hp-paper shapes sit under everything the plan draws. Since v1.58.0 the
// paper is the ROOM CONTOURS and ONLY them — one shape per room, never their
// bounding box (section 12), and never the backdrop image rect either
// (docs/BACKDROP.md §3). The demo's f1 IS an image plan, so this section now
// (docs/DECOR-EDITOR.md §3.3). The demo's f1 IS an image plan, so this section now
// asserts the new rule on exactly the case that used to be the exception.
// The scene colour is visible ONLY around the paper. The four-phase
// environment changes only outside it and adds an alpha-aware outer outline.
+2 -2
View File
@@ -151,8 +151,8 @@ const out = await page.evaluate(async () => {
// the cover, which is what this section pins.) Same cause, same helper:
// _stateClass and
// the icon morph read d.primary, so the plan reported the state of
// `switch.*_reverse_direction`. The rule (docs/FILTERING.md «What a marker
// SHOWS»): the marker indicates the entity its tap ACTS ON — the cover
// `switch.*_reverse_direction`. The rule (docs/DEVICE-PRESENTATION.md «Source precedence: what a marker
// shows»): the marker indicates the entity its tap ACTS ON — the cover
// exactly when the owner has explicitly chosen «Открыть/закрыть».
const devEl = (dev) => {
// The viewport may refit asynchronously after an editor transition. Use
+1 -1
View File
@@ -225,7 +225,7 @@ const res = await page.evaluate(async () => {
await popoverPicker?.updateComplete;
// seven tools (the six drawing ones + «Мебель», docs/FURNITURE.md) plus
// «Картинка-подложка», which f1 offers because it HAS a picture
// (docs/BACKDROP.md §2); a hand-drawn space still shows seven
// (docs/DECOR-EDITOR.md §3.2); a hand-drawn space still shows seven
out.toolBtns = sr().querySelectorAll('.decorbar .btn.dtool').length === 8;
// 2) нарисовать прямоугольник drag-ом (через прямые вызовы)
c._decorTool = 'rect'; c._decorStyle = { color: '#ff0000', width: 3, fill: true }; await c.updateComplete;
+2 -2
View File
@@ -89,7 +89,7 @@ const res = await page.evaluate(async () => {
const y0 = tsp[0] ? +tsp[0].getAttribute('y') : NaN;
const y1 = tsp[1] ? +tsp[1].getAttribute('y') : NaN;
out.blockCentredVertically = Math.abs((y0 + y1) / 2 - ay) < 0.01 && y1 > y0;
// длинная строка НЕ переносится сама (docs/LIVE-TEXT.md: никаких автопереносов)
// длинная строка НЕ переносится сама (docs/DECOR-EDITOR.md §5: никаких автопереносов)
open(multi); await c.updateComplete;
c._decorTextDialog = { ...c._decorTextDialog, text: 'x'.repeat(120) };
c._decorSaveText(); await c.updateComplete;
@@ -109,7 +109,7 @@ const res = await page.evaluate(async () => {
&& frame().querySelectorAll('.dthandle').length === 5
&& !!frame().querySelector('.dtrot');
// --- визуал в 4 раза меньше, хит-зона прежняя (docs/LIVE-TEXT.md §3) ---
// --- визуал в 4 раза меньше, хит-зона прежняя (docs/DECOR-EDITOR.md §5.3) ---
out.fiveVisibleKnobs = frame()?.querySelectorAll('.dtknob').length === 5;
const rOf = (sel) => { const e = frame()?.querySelector(sel); return e ? +e.getAttribute('r') : NaN; };
const hitR = rOf('.dthandle.dtrot');
+1 -1
View File
@@ -1,4 +1,4 @@
// Живой текст в декоре (docs/LIVE-TEXT.md): все связи с HA живут прямо в
// Живой текст в декоре (docs/DECOR-EDITOR.md §5): все связи с HA живут прямо в
// тексте как {entity} / {entity:attribute}. Один блок может смешивать обычный
// текст и несколько переменных; выбор значения вставляет токен в позицию
// курсора. Старые entity/attr/unit читаются, но первое сохранение мигрирует их.
+1 -1
View File
@@ -1,4 +1,4 @@
// Значения форматирует HOME ASSISTANT (docs/STYLING-HOOKS.md §6, docs/LIVE-TEXT.md §2.1).
// Значения форматирует HOME ASSISTANT (docs/STYLING-HOOKS.md §6, docs/DECOR-EDITOR.md §5.2).
// Везде, где карточка печатает состояние ОДНОЙ сущности, она зовёт
// hass.formatEntityState (и hass.formatEntityAttributeValue для атрибута) —
// значит, работают display_precision, локальный разделитель дробной части и
+51 -2
View File
@@ -9,8 +9,7 @@ of the shipped integration.
stand picks it up automatically from this path
(`/opt/hp/bin/hp-update-dev.sh`, which calls the same script). The rest of the stand-only config
(template LQI sensors, alarm helpers, the smoke automation) lives in the
seeds on the stand host — see `docs/TESTING-DEMO.md` and the memory note
`houseplan-demo-stand`.
seeds on the stand host; the demo home itself is described below.
- `demo_guard/` — stand-only guard (2026-07-31). Visitors log in as an
administrator (the card editor is gated on `is_admin`) and kept restarting
@@ -56,3 +55,53 @@ refuses anything that does not have exactly one
manifests turned the Hassfest job of PR #9004 red on 2026-08-11, five weeks into
the review queue. `test/repo-hygiene.test.mjs` now fails if a second one
appears, so this cannot be rediscovered by a reviewer again.
## The demo home
**https://demo.houseplan.tech** — public, login `demo` / `demo` (an
administrator). **https://dev.houseplan.tech** — the closed stand behind basic
auth (access with the owner), auto-deploys `dev`. The public stand **resets
every hour** to a pristine synthetic home — break it freely, delete rooms,
upload plans, an hour later everything is back (the countdown is printed to the
DevTools console). Long-lived checks («the file is gone a day later», «survived
a restart») therefore cannot live on the stand, and restarting HA from inside is
blocked by `demo_guard`.
Demo home v2 is an anonymised layout of a real country house: dashboard
«House plan» (`/house-plan/0`; views Plan / Kiosk / Schema) and three spaces —
**Ground Floor** (Kitchen & Living, Hallway, Guest Bedroom, Guest WC, Boiler
Room, Sauna, Under-Stairs Closet, Outdoor Storage — hand-drawn, thick hatched
walls), **First Floor** (Kids Room A, Kids Room B, Upstairs Hall, Kids
Bathroom, Master Bathroom, Bedroom, Study — with a plan image backdrop) and
**Yard** (the Yard room plus a decor outline of the garage). 97 markers in
total: ~51 on Ground Floor, 23 on First Floor, 2 in the yard. Key demo devices:
the robot vacuum `vacuum.demo_robot` (dock in Under-Stairs Closet), leak
sensors (toggled by the helper `input_boolean.demo_leak`), smoke sensors (fire
by themselves every 10 minutes), the boiler and the water tank in Boiler Room
(value markers plus live text labels), the Front Door and Terrace locks,
curtains in Study, the garage gate in Yard, a TV with speakers, the kitchen
hood, the air conditioner in Bedroom, the composite Smart Plug, the
permanently unavailable Pantry Light, and the weather
`weather.demo_weather_south` (sun in the windows and the day/night cycle).
Everything is in English; the demo user's language is Auto, so the interface
follows the browser. **Smart Plug 2** is deactivated in the HA registry on
purpose — it is the ready-made example for the disabled-devices behaviour.
The public demo user is an administrator, so the full registry scenario can be
checked on the stand; a limited/read-only user needs the local harness or a
separate unprivileged user.
## What the stand cannot show
- Real Zigbee/Z-Wave hardware, real robots (Dreame/Xiaomi/Valetudo), a real
wall tablet.
- The HACS install/update path, YAML-mode Lovelace, reinstalling the
integration while keeping its config.
- Restarting HA from inside — blocked by `demo_guard` (the hourly container
reset is the only «restart»).
- Anything that needs the stand's file system: broken stores, `kill -9`
between writes, unreadable folders, watching process memory.
- Long-lived scenarios (a day-long sweep, «an hour later») — the reset comes
first.
- A non-admin user (`admin_only`, hidden tabs) — the stand has the single
admin `demo`.