Repository navigation
fix(pin): hold garage covers by device class and through groups (#773) - #792
Merged
Merged
Conversation
The recommended cover rules matched door/gate/garage in the entity id only, so a cover with device class garage and a plain id, or a cover group holding the garage door, opened with no PIN on every path. expand_critical_rules adds, per entity_match rule, a copy naming each entity whose device class contains the word and each group (cover group or old-style group, any depth) holding a matching entity. The tool-call gate and add_automation extend their rules before screening, so intents, scripts, scenes and automation contents all see the same reach. Fixes #773 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Review of the first fix found it saw only loaded entities: - a registered garage cover not loaded yet, an unavailable cover group (no member list) and MQTT/ZHA groups (group_entities) were missed, so add_automation could install an automation that opens the garage later; - a relay cover named "Garage Door" with no device class still ran. The rule word now matches the device class exactly, or a whole word in the friendly name, registry name or alias; the index reads the entity registry, group_entities and group helper config entries, holds a group helper whose members cannot be read, and walks groups through a member-to-group index in one pass. The security audit's webhook automation check uses the same extended rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
Second review cycle: - a rule word now matches its plural and is read with `_`/`-` as spaces, so "Driveway Gates" and a rule written as `garage_door` match names; - an unloaded entity is known by its device's name too; - a group member not yet loaded or registered is matched by its id, and an unloaded entity in the rule's domain with no device class is held (it could be an MQTT/ZHA group whose members are unseen); - an id seeds groups only when the word starts it or a part of it, so a group of outdoor awnings is not held for "door"; - CONF_ENTITIES, docstrings, docs and the audit's stale "mirroring" note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
Third review cycle:
- each rule copy for one entity matches that id exactly through an
internal marker (stripped from stored rules) and one shared predicate,
so a held leftover cover.living_room no longer holds its blinds and
cover.a no longer holds cover.awning; the copy keeps the user's word
so a reader unaware of the marker still sees the original rule;
- restored startup placeholders count as not set up; disabled entities
are never held; a group member HA does not know holds its groups;
- names match the word with no letter next to it ("Gate1"), and a
window-covering device class is not matched by name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
This was referenced Oct 18, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
…at a call holds (#773) Re-review fixes (gstack review, 3 cycles): - Device class matches against every class an entity has (state, user "Show as", integration's original); only the integration's own class can mark a window covering and turn off the name match. Unregistered entities read it from the loaded entity, so YAML `customize` does not count. - An entity not loaded is held unless its integration classes it as a window covering (an unloaded MQTT/ZHA group classed shutter is held). - One entity that raises while being read is logged and held instead of failing every guarded call; a warning when there is no hass. - Group members match by the word anywhere in the id, as direct calls do, so a group holding cover.backdoor is held like the cover. - The network audit extends rules only when an automation has a public webhook. - Tests: Show-as and customize, unloaded classed group, unreadable entity (held and logged), no-hass warning, script through the group, add_automation fail-closed, the audit's lazy path with a positive control, the collector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
This was referenced Oct 18, 2026
goruck
added a commit
that referenced
this pull request
Oct 18, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #773. The recommended Critical Action rules match
door,gateorgarageagainst the cover's entity id only, so a cover with a plain id (cover.msg100_abc) opened with no PIN even when its device class or name said garage, and so did a cover group likecover.all_coversholding the garage door, directly, by name, in a scene, or in an automation the agent wrote.Fix
New
agent/rule_entities.py:expand_critical_rules(hass, rules)builds one index of states, the entity registry and the device registry, and for eachentity_matchrule adds a copy matching one entity exactly (internal_exact_entitykey, stripped from stored rules; read through one sharedrule_entity_matchespredicate inmatches_critical_ruleand_entity_is_guarded) for each entity in the rule's domain that:entity_id, MQTT/ZHAgroup_entities, group helper config entry) holding a match, at any depth;Applied in the tool-call gate (
_critical_action_guard),add_automation, and the audit's webhook-automation check. A failure in the extension fails closed at the gate; the audit falls back to the configured rules.Known gaps (documented)
switch_as_xcover (Critical Action PIN: a garage cover's underlying switch (switch_as_x) or a template cover's action opens it with no PIN #794)._is_criticalmust callexpand_critical_rulestoo.Review
gstack review, 3 cycles (specialists, Claude adversarial, Codex, red team each cycle); not converged after cycle 3's fixes, which were not re-reviewed.
Release
A safety gate not operating, so per RELEASING.md this ships alone as a patch.
Tests
test_cover_group_pin_gate.py(59).make lint,make test(5222 passed),make typecheckclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01SFM2hXVUELSh13U7FEKGJu