Files
houseplan-card/CONTRIBUTING.md
T
Claude 2a62ad5b95 process: derived artifacts are accepted on dev once per beta (#697)
The screenshot fingerprint and golden baselines stop being a tax on every
task branch:

- Task branches no longer commit docs/images/** or golden baselines. On a
  branch the screenshot freshness stays a preflight warning; the review
  prompt, REVIEWER.md and AUTHOR.md drop check-docs as a per-task gate.
- beta-derived.yml refreshes them on dev in one bot commit before the beta
  candidate: canonical docs capture + docs:accept --reviewed, golden from
  the golden-images artifact of a completed Validate on dev +
  golden:accept --reviewed. A changed frame or scene is accepted only when
  named in the inputs; undeclared differences refuse. Baseline commits carry
  Release: and Baseline-Reviewed:; the subject is not a candidate subject.
- classify-changes: the Release: trailer on an issue/* branch no longer
  switches on the heavy set. ci:full / ci:golden do: process-track emits
  full=true, the review gate dispatches Validate with full=true and does not
  accept a light proof.

Canon: PROCESS.md §3 п.13, §5.1, §8, §11.4; CONTRIBUTING.md.

Issue: #697
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-28 23:38:50 +03:00

7.7 KiB
Raw Blame History

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:

  1. src/i18n/<code>.json for the card;
  2. custom_components/houseplan/translations/<code>.json for the Home Assistant integration;
  3. 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 on the pinned Chromium in CI. A task branch does not commit them, nor the golden baselines: once per beta the beta-derived.yml workflow refreshes the fingerprint, the frames and the golden baselines on dev in one bot commit, accepting only the frames it was told to expect (PROCESS.md §8, #697). A task that changes visuals on purpose sets the ci:golden label. The manual path — the Docs screenshots workflow and npm run docs:accept -- --reviewed --from=<unpacked artifact>, or --identical when no pixel can move — stays for the release manager. 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.md for state changes; docs/DEVELOPMENT.md for 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:release in a commit with a Release: trailer (#657). An ordinary task restores it with npm run bundle:clean before committing — the commit-msg hook refuses a bundle change otherwise.
  • Tap actions have a security model (locks/alarms never toggle from the plan) — see resolveToggleIntent in src/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.yaml tracks 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.