ha-home-keeper
Home Keeper is a Home Assistant plugin for tracking home maintenance and chores with deep HA integration
Install / Use
npx skills add prestomation/ha-home-keeperInstalls into whichever agent you are using.
Amazon Q Rules
Amazon Q Developer rules
Quality Score
Category
AutomationSupported Platforms
Skill content
View source on GitHubHome Keeper — testing & workflow conventions
Git & PR workflow
- Never push directly to
main. Work on a feature branch and open a PR; squash merge. - Update
CHANGELOG.mdfor every user-facing change before a release. - Post screenshots to the PR for any change that adds/changes/fixes UI (capture
via
tests/e2e/screenshots.capture.ts, commit underdocs/images/, embed via araw.githubusercontent.com/.../<commit-sha>/docs/images/<file>.pngURL). - The video walkthrough is a CI build artifact, never committed — for a PR that
adds a new user-facing UI feature, CI keeps it current (bug-fix/styling PRs need
only screenshots).
walkthrough-preview.ymlruns the capture harness (tests/e2e/walkthrough.capture.ts→walkthrough.config.ts, wrapped byci/capture-video.sh) on every PR, transcodes to gif+mp4, publishes them to thegh-pagespr-preview-media/pr-<n>/umbrella (GitHub Pages), and posts a sticky PR comment embedding the gif with an mp4 link.docs/videos/is gitignored, so there's zero git bloat. The author's gate is editing the tour: extendwalkthrough.capture.tsfor a new surface in the same PR and confirm the regenerated comment shows it; capture is a soft gate (a flaky run posts a failure note, doesn't block). Runci/capture-video.shlocally only to debug the tour. - Document new major features in
README.mdin the same change — add a brief section covering the use cases (what problem it solves) and a little about how it's used, with screenshot(s) (capture via the Playwright harness, commit underdocs/images/, embed in the README with a relativedocs/images/…path). A new headline feature isn't "done" until the README shows it. (The moving walkthrough is not committed to the README — it's the per-PR CI comment above.) - User-facing prose is linted for AI-tell phrasing.
lint.yml'svalejob runs the vale-ai-tells style (pinned version in.vale.ini) overREADME.md,CHANGELOG.md, the canonicaldocs/*.md(excludes*_PLAN.md/research scratch docs),website/docs/intro.md,strings.json,services.yaml, andlocales/en.json(not the other locales ortranslations/, since the rules are English-phrase regexes). It's diff-scoped (filter_mode: added), so only new/changed lines can fail CI. The existing corpus is cleaned up separately. Run locally withvale sync && vale <paths>. Disable an accepted false positive per-file in.vale.ini(ai-tells.RuleName = NO) or inline with<!-- vale ai-tells.RuleName = NO -->/... = YES -->. For example,services.yamldisablesColonUsage, which otherwise fires on every unquoted YAMLkey: Valueline. Diff-scoping misses pre-existing hits on lines a full-file prose rewrite happens to move, so runvale <file>yourself first for that case. The pinnedai-tells.zipversion has no bump automation (Dependabot/Renovate don't track raw release URLs), so bump it by hand periodically.
Tests (run locally before pushing — never use CI as the test runner)
- The recurrence engine and model are the correctness core: keep them HA-free and
thoroughly unit-tested.
pytest tests/unitmust run without the HA harness.tests/conftest.pyexecutes the pure modules under their real dotted name (custom_components.home_keeper.<mod>, with stub parent packages so the HA-importing__init__.pynever runs) and registershk.<mod>/hk_<mod>as aliases. Two invariants there: mutmut matches a mutant's path-derived key against the function's__module__, so executing them ashk.<mod>would make every mutant look untested; andhkmust stay a distinct package object, not an alias ofcustom_components.home_keeper, becausefrom . import xresolves through the parent's__name__— aliasing them makes the modulestest_coordinator_purge.py/test_calendar.pyload ashk.coordinatorpull in the real HA-importing siblings instead of their fakes. - Layers:
tests/unit(pytest, pure logic),tests/frontend+frontend/test(vitest),tests/integration(Docker HA),tests/e2e(Playwright),tests/upgrade(two-phase HA version upgrade). Run e2e/integration withbash ci/e2e-up.sh/ci/test-python-integration.sh; stage the upgrade suite's fixtures withbash ci/fetch-glues.shfirst. - A panel assertion is not coverage for a native entity. The panel and the
todo/calendarentities are separate projections of the same store, so the panel being right proves nothing about them. #221 shipped with a passing e2e test that created a one-off, completed it, and asserted the panel filed it under Completed — while the to-do entity went on offering it asneeds_actionforever. A state change that should be visible on a native surface needs an assertion on that surface. - Assert disappearance, not just appearance. Presence gets asserted by accident;
absence has to be asked for, and the interesting bugs are absences that didn't
happen. Test a state transition from both ends — present before, gone after — via
expectAbsentFromActiveSurfaces/expectOnTodoListintests/e2e/tests/helpers.ts. Asserting only the post-state also passes for a task that was never listed at all. - A screenshot is documentation, not verification. The capture harness wrote
docs/images/4-usage-todo-and-calendar.pngshowing #221 in plain sight — stale to-do items beside panel columns marking those same tasks Completed — for months. Capturing a surface is not covering it; if a screenshot shows a surface, something should be asserting on it too. - An e2e spec owns what it creates. The container's task store is the committed
seed fixture (
tests/integration/ha_config/.storage/home_keeper), so anything a spec leaves behind is a permanent addition to it. Register created ids and delete them inafterEach(createTask/deleteTaskinhelpers.ts), and give fixtures stable names — aDate.now()suffix makes each leak look like a new record instead of the same spec failing to clean up, which is how eight of them reached git. - Anything that rests on an HA framework contract — device registry, entity registry, device automation — needs an integration-level assertion. Unit tests mock the framework away and cannot see the contract change. #183 (devices split per config entry in HA 2026.8) shipped because the only device-attachment coverage was for the self-owned case, never the foreign-device one.
- Cross-version behaviour needs an upgrade test, not just a fresh-boot test.
tests/upgradeboots a frozen pre-split HA, seeds every scenario into one config dir, then boots the current HA against that same dir so HA runs its own migration in between — two cold starts for the whole suite. The pre-split tag is a frozen pin: it defines "the world users upgrade from", so bumping it changes the meaning of the test. - A test must exercise the shipped function, never a copy of it. Re-implementing
the logic under test inside the test file (to dodge an import) proves nothing: the
production code keeps zero coverage and every later edit to it stays green. An
HA-importing module is still unit-testable —
test_calendar.py,test_coordinator_purge.pyandtest_device_heal.pystub the HA symbols the module imports, register fakes for its HA-aware siblings, load the real file underhk.<mod>, then inject fakes by patching the loaded module's bindings. Follow that pattern instead of duplicating the source. - Check that a new test can fail. Mutate the line it covers and confirm it goes red before relying on it. A test whose fake can only produce the passing case (e.g. a mock registry that returns one candidate, "verifying" a preference between several) is worse than no test: it reports coverage the code does not have.
- Never commit a real
.storagedump as a fixture. Production snapshots carry serial numbers, MAC addresses, document links and other household data, and they live forever in git history. Build fixtures from synthetic data, and wire every fixture into a test — an unreferenced fixture is only a leak with no upside. - Known-broken contracts get
xfail(strict=True), never a weakened assertion. The test then documents the breakage without going red, and becomes a hard failure the moment a fix lands, forcing the marker off. - HA versions: PRs run
stable(HA_TAGintests/integration/docker-compose.yml);ha-beta.ymlrunsbetanightly as an early warning and gates nothing. - After running the Docker HA container locally, restore the seeded fixtures
(
tests/integration/ha_config/.storage/{home_keeper,core.config_entries}); don't commit runtime-mutated state. - A second delivery path needs a test that deletes the first one. #228 was
invisible to a suite where every dashboard test loaded a freshly-rendered app shell
that happened to carry the card's import. Don't wait for a stale cache — reproduce
what one is:
tests/e2e/tests/card-registration.spec.tsintercepts the dashboard navigation withpage.route, strips the card'simport(...)out of the HTML, and asserts the card still renders. Two things make it honest. It setstest.use({ serviceWorkers: 'block' }), because a service worker answers navigations beforepage.routesees them, and HA registers one on first load — so without it the reload that follows is served the original shell and the test passes for the wrong reason. And it asserts the unstripped HTML did contain the import, so the test cannot quietly go vacuous if HA changes how it delivers extra modules. It deliberately does not useopenCardDashboard: that helper reloads up to 3x to absorb cold-frontend flake, which here would only re-serve the stripped shell while turning a precise failure into an opaque timeout. - Verify a browser-sensitive e2e spec with the browser CI actually uses.
e2e.ymlrunsnpx playwright install chromiumand noCHROMIUM_EXEC, so CI drives Playwright's headless shell; theCHROMIUM_EXECoverride documented in AGENTS.md for the Claude Code remote environment points at a different, older full Chromium.card-registration.spec.tspassed locally and failed on CI three times for exactly that reason. Re-run a spec withCHROMIUM_EXECunset (CI=true npx playwright test <spec>) before trusting it. - A spec that rewrites a document needs the Local Network Access flag. Chrome classifies a
response synthesized by
route.fulfillas coming from a public address space, then blocks the page's ownws://localhost:8123/api/websocketas a local-network request (net::ERR_BLOCKED_BY_LOCAL_NETWORK_ACCESS_CHECKS). The frontend never connects, so nothing websocket-delivered — Lovelace resources included — ever loads, and the failure looks like the feature under test is broken. Pass--disable-features=LocalNetworkAccessChecksin that spec's owntest.use({ launchOptions }), not inplaywright.config.ts: only a spec that rewrites a document needs it, and every other spec should keep the check so a future test of network or CORS behaviour still gets it. NotelaunchOptionsreplaces the config's copy rather than merging, so the spec has to re-plumbCHROMIUM_EXECitself — seetests/e2e/tests/card-registration.spec.ts. - Give an e2e assertion that depends on browser plumbing a failure message that names what it
saw. The above took a CI round-trip per guess until the spec captured console errors and
whether the bundle was requested at all; "the bundle was never requested" is the line that
ended it. A bare
waitFortimeout says only that something, somewhere, did not happen.
Mutation testing (a PR gate)
Coverage proves a line ran; mutation testing proves a test woul
Truncated for display — read the full file on GitHub.
Related Skills
pyspark-etl-best-practices-cursorrules-prompt-file
40.6kCursor rules for PySpark ETL development with code style, joins, window functions, map operations, and Iceberg patterns.
semiotic-react-dataviz-cursorrules-prompt-file
40.6kCursor rules for Semiotic data visualization library with 30+ chart types, MCP server, and AI-assisted chart generation.
Agent-Reach
74.2kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
ruflo
68.9k🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
Security Score
Audited on Invalid Date
