Волна 3 эпика #674. AGENTS.md 650 → 187 строк: карта пакета, маршрут чтения, правило №1, классы и треки одной строкой со ссылками, трейлеры, рабочие деревья, хендофф и ожидание вердикта; пересказы PROCESS.md — ссылками на разделы. Неверный список «Gate jobs» снят (списки jobs не копируются в прозу, шапка PROCESS.md). Правила, жившие только в AGENTS, получили дом: жёлтый вердикт при выполненных AC — PROCESS §2.7; свежесть бандла, съёмка только в Linux (#455, HP_ALLOW_FOREIGN_CAPTURE) и смоки из AC до S7 (#151) — TESTING.md; причуда демо-стенда и среда-зависимый smoke_opening_measure — DEVELOPMENT › Smoke tests; отказ публикации без `Release:` и при несвежем отпечатке бандла, отмена Validate новым пушем, кандидат беты не promotion-only, fail-closed реестра Labs — DEVELOPMENT; предупреждение и ошибка свежести скриншотов — CONTRIBUTING. PROCESS.md: §13 (внедрение с открытым ⏳), §14 (блок со ссылкой на несуществующий docs/PROCESS.md) и §7.3 (история) удалены. Ссылки «§7.2» на правило полного разбора после ребейза ведут в §2.10, на сверку SHA перед выводом — в §2.7; то же в сообщениях scripts/branch-state.mjs, merge-candidate.mjs, review-doc-guard.mjs, pre-push-gate.mjs, в промпте _process.yml и TESTING.md. Число `any` в прозе → `node scripts/no-new-any.mjs --total` (новый режим, юнит-тест; было «1034 в 49 файлах», сейчас 862 в 52), дата-число замороженного списка якорей монолита снято. Устаревшая команда пересъёмки скриншотов в §8 заменена ссылкой на действующий путь. STATUS.md 113 → 61 строка: сгенерированный снимок, текущий цикл и девять строк решений; Workflow, CI, Toolchain, Tests, Scope, open items и политика документации — ссылками (PROCESS §2.6, DEVELOPMENT › Release, TESTING); локали en/ru/de/fr; закрытые «coverage, mypy strict» сняты. DEVELOPMENT.md: file-sync и «Reproducible scripts» (прототип) удалены; раздел Release — единственный дом релизной механики: введение, правила тела стабильного релиза (#328, release:notes), шаг continuity:screencast, источники версии по release-contract. CONTRIBUTING: ссылка на Release вместо пересказа, замеры клона без чисел. TESTING: any-гейт — ссылкой на PROCESS §8. entry-cost: автор 11 125 → 5 407 слов, ревьюер 8 464 → 4 285. Issue: #680 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
7.5 KiB
Contributing to House Plan
Thanks for your interest! The project is one HACS package: a storage integration
(custom_components/houseplan/, Python) and a Lovelace card (src/, TypeScript + Lit).
Changelog
User-visible changes go into both changelogs in the same commit:
docs/CHANGELOG.md (English) and docs/CHANGELOG.ru.md (Russian). Entries
older than v1.42.0 exist only in the English file — no need to backfill them.
Translations
A shipped UI language has three matching parts:
src/i18n/<code>.jsonfor the card;custom_components/houseplan/translations/<code>.jsonfor the Home Assistant integration;- one registry entry (code, native label and eager dictionary or lazy loader)
in
src/i18n/registry.ts.
Use the canonical Home Assistant/BCP 47 language tag as <code> (for example,
fr or pt-BR) and use that exact spelling for both JSON filenames. Lookup is
case-insensitive and also accepts _ from legacy locale sources.
The registry drives language resolution, the visual-editor selector and parity
tests. The tests reject missing or extra locale files; frontend dictionaries
also fail on mismatched keys, empty values and changed placeholders.
Placeholders such as {name} and {n} are a contract: do not translate, add
or remove them.
English and Russian are synchronous fallback/legacy locales. German is the
reference lazy third-locale implementation: its module carries the same build
fingerprint as the entry bundle, has one content-hashed retry URL and is listed
under lazyLocaleFiles in houseplan-assets.json. New sizeable locales should
follow that path unless a measured initial-bundle budget explicitly justifies
an eager import. Extend the runtime, manifest, file/key/placeholder parity and
regional-locale tests together; never bypass the registry by importing a locale
directly in a component.
Administrator-only copy lives in three lazy namespace dictionaries —
src/i18n/settings/, src/i18n/support/ and src/i18n/topology/ (#627). Their
English file is static (the synchronous fallback); every other language is one
lazy chunk per namespace × language: a two-line loader module
src/i18n/<namespace>/<namespace>-<code>.ts, one import() with a
content-hashed retry token in src/i18n/<namespace>.ts, and one entry in
NAMESPACE_LOCALE_CHUNKS in scripts/bundle-manifest.mjs. The lazy surfaces
(onboarding, editor runtime, Zigbee overlay) wait for their dictionaries before
painting; never import a non-English namespace JSON statically — the budget
gate and test/i18n-lazy-namespaces.test.mjs refuse it.
The current subst() helper does not implement plural rules. Phrase strings so
their grammar does not depend on the numeric value (for example, use a neutral
label followed by {n} rather than an English singular/plural pair).
Adding a UI locale does not automatically create another full documentation set; maintain the existing English and Russian documentation according to the project's normal rules. Right-to-left layout is a separate product project, because the plan canvas and editors cannot be mirrored by translations alone.
Documentation screenshots
The images under docs/images/ are produced only from synthetic data by the
Docs screenshots workflow (demo/docs/capture.mjs on the pinned Chromium)
and accepted locally with npm run docs:accept -- --reviewed --from=<unpacked artifact>; when a change cannot move a pixel, npm run docs:accept -- --identical re-captures locally, compares decoded pixels and refreshes only the
source fingerprint. Scenario version, source fingerprint and every image hash
are recorded in the screenshot index, and
node scripts/check-docs.mjs reports a stale fingerprint: a warning on an
ordinary push, an error on a beta candidate (a commit with a Release:
trailer). The full rule is in PROCESS.md §8.
Where to ask
Not sure whether something is a bug, or just want to discuss an idea before writing code? The Telegram chat @ha_houseplan is the quickest route to the author and other users. Bugs and concrete feature requests still belong in issues.
Backlog and work status
GitHub Issues are the only
active backlog. An issue owns scope and acceptance criteria; its labels own
priority and workflow status — PROCESS.md §9 holds the vocabulary. Before
starting planned work, link it to an existing issue or create one, and keep it
current until the verified result is closed. Design specs and ADRs may support an
issue, but they do not replace it or maintain a separate checklist.
Five-minute setup
git clone --filter=blob:none https://github.com/Matysh/houseplan-card && cd houseplan-card
npm ci # frontend toolchain
npm run typecheck # tsc --noEmit (strict)
npm test # node:test — pure logic, i18n parity, tap-action security
npm run build # tsc + rollup → dist/houseplan-card.js
pip install pytest voluptuous && python -m pytest tests_backend -q # pure backend tests
npm install # also installs .githooks through the prepare script
Why --filter=blob:none (#345)
A blobless clone keeps every commit and tag, so ranges, merge-base and
git diff across history work exactly as in a full clone; only historical file
contents are fetched on demand. Most of the pack is exactly such content that
almost nobody reads again — documentation screenshots re-captured with the UI and
the committed bundle rewritten by every release candidate — so a blobless clone
is several times smaller. To see the numbers for your own clone, compare
git count-objects -vH in a blobless and in a full one.
Drop the flag if you work offline with history, or need git log -p over the whole
tree repeatedly. Do not replace it with --depth=1: a shallow clone is about
the same size but has no merge-base, so the process gate, smoke-select and
every origin/dev..HEAD range stop working.
The HA-harness backend tests (tests_backend/test_ha_*.py) need the
repository-pinned Python (.python-version) and
pytest-homeassistant-custom-component home-assistant-frontend; their canon is
Linux CI or WSL (bash scripts/wsl-setup.sh --verify). Without an importable
homeassistant they are not collected at all — not skipped — and pytest prints
HA harness NOT collected: … (#630).
Ground rules
- Docs in the same commit: CHANGELOG entry for user-visible changes;
docs/STATUS.mdfor state changes;docs/DEVELOPMENT.mdfor new gotchas. - Every UI string goes through
src/i18n/<lang>.json; follow the Translations flow for registry and backend parity. - The committed bundle changes only in a release candidate:
npm run bundle:releasein a commit with aRelease:trailer (#657). An ordinary task restores it withnpm run bundle:cleanbefore committing — thecommit-msghook refuses a bundle change otherwise. - Tap actions have a security model (locks/alarms never toggle from the plan) —
see
resolveToggleIntentinsrc/device-toggle.ts; don't weaken it. - Every commit follows the issue and trailer contract in
PROCESS.md. - Follow the Integration Quality Scale where applicable —
custom_components/houseplan/quality_scale.yamltracks the self-assessment.
Architecture
Start with docs/ARCHITECTURE.md (data model, WS API, coordinate system) and
docs/STATUS.md (current state). Release mechanics live in one place:
docs/DEVELOPMENT.md › Release.