You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: dashboard search stays on legacy path without false partial (#2020)
* fix: Keep dashboard search_types off the component path and stop counting auto-generated dashboards as scan failures
ha_search(search_types=["dashboard"]) was forwarded to the ha_mcp_tools
component, whose search command has no dashboard surface — the request
bounced off the component schema into a warning-laden legacy fallback on
every call. Requests naming a surface the component lacks now stay on
the legacy path silently, like the other route-ineligible modes;
supported-type pins keep the component fast path.
The legacy dashboard scan also counted auto-generated dashboards
(never taken control, lovelace/config answers config_not_found) as
failed scans, so a stock install reported partial: true with
"N dashboard(s) not scanned". A config-less dashboard has nothing to
scan and now reads as a clean no-match.
Fixes#2008
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Only skip config-less dashboards, not unresolved ones; pin routing gate to component schema
Review round: config_not_found is HA's code for BOTH "No config found."
(auto-generated dashboard — clean skip) and "Unknown config specified"
(dashboard deleted since the registry-list snapshot — a genuine gap), so
the skip now matches the message too, via the canonical error extractors.
A lockstep contract test pins the routing gate's allowlist to the real
component schema and the public search_types vocabulary, since the silent
legacy route removed the warning that would have exposed future drift.
Also: seam test composing both fixes through ha_search, e2e assertions
against the real component, and a stale client-envelope comment corrected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* chore: Remove accidentally committed review scratch file
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Serve ha_search's dashboard bucket from the component's in-process search
The ha_mcp_tools component already answers a cross-dashboard search in
one in-process frame, and ha_config_get_dashboard(mode="search") routes
through it — ha_search's dashboard surface never did, fanning out one
lovelace/config read per dashboard even with the component installed.
The bucket now rides the component search for the exact-match, no-body
shape (the default ha_search call), with the legacy walk kept for fuzzy
scoring, include_config bodies, truncated frames, and every component-
fallback case. YAML-mode dashboards, which the component deliberately
never scans (their bodies can carry resolved !secret values), surface as
an explicit partial reason instead of looking exhaustive.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Use the registry row title in component-served dashboard records
Live parity check: the component match only knows the config body's
(often absent) title, so records fell back to the url_path where the
legacy walk emits the registry row's title. The rows are already
fetched for the YAML exclusion count — join them for the title.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Whole-document dashboard search verdicts from the component (1.3.0)
Codex review round on the component wiring: the card-scoped matches walk
narrowed ha_search's dashboard coverage (a view-title-only hit vanished),
the yaml exclusion count missed a YAML-mode default dashboard (no list
row), and fail-soft load errors made unreadable dashboards look clean.
The component's search mode now also emits document_matches (a per-
dashboard whole-config substring verdict, ported leaf-for-leaf from the
server's _search_in_dict), yaml_skipped counted in-process over the full
map, and load_failed with ConfigNotFound kept a clean skip. The server
requires the new dashboards_doc_search capability (component 1.3.0) and
those keys, or falls back to the full legacy walk — a pre-1.3.0
component never narrows coverage. Registry titles ride a separate doc
key so MODE 4 match-record parity keeps the body title.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Align capability/version pins and drop an unused type-ignore
CI round: the info drift guard and manifest parity test pin the literal
capability list and version — updated for dashboards_doc_search / 1.3.0.
The guarded ConfigNotFound import resolves to Any under mypy's
homeassistant override, so the assignment ignore was unused.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Component version 1.2.4, not 1.3.0
The dashboard search coverage was supposed to be there all along, so
this is a patch on the pending version line, not a new-feature minor.
Capability negotiation (dashboards_doc_search), never the version,
gates the server routing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Label the default dashboard "Default Dashboard" on the component path
Codex round: the legacy walk always emits ("default", "Default
Dashboard") for the built-in dashboard; a taken-control default can
carry its own title, which the component-served record must not leak
into the envelope — record-shape parity between the two paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Single-assignment title selection to satisfy mypy narrowing
The branch-assigned default-title fix typed `title` as str in the first
branch and Any | None in the second ([assignment] under strict mypy).
One conditional expression keeps the inferred type consistent; an empty
component title now falls back to the url_path, which reads better than
an empty label anyway. Also records the pending-version evidence (stable
v7.14.2 ships 1.2.3) on the version pin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* test: Drop literal component-version pins
The lockstep invariant (manifest == COMPONENT_VERSION) is the test;
literal pins only forced churn on every bump and bred release-cycle
narration in comments that rotted the moment the next stable shipped
(the "Pending 1.2.3" claim that misled a review round). Bump rules
live in AGENTS.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* test: Restore the literal version pin, single-sited with a timeless rationale
The literal is protection, not churn: a wholesale accidental downgrade
reverts manifest and const together, passing pure lockstep — only the
pin catches it, and it keeps every version change a conscious,
review-visible edit. One site (the parity test) carries it; the
duplicate pin stays removed, and the comment states the invariant
without narrating stable/pending state that rots between releases.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* ci: Guard the release-cycle version invariant at PR time and release time
A PR that changes the component while its version equals the mirror's
released stable would merge changes onto a shipped version: the stable
tag step skips existing tags and dev pre-release tags sort below the
equal stable, so the changes silently reach nobody. Two layers close
it: a PR-level Component Version Gate (fires only on a provable
component diff, fails open with a warning when the base or mirror API
is unreadable) and a loud release-time backstop — the mirror's stable
tag step now diffs an already-tagged version against the snapshot and
fails when content drifted, which also covers the race a PR check
cannot see (stable shipping while a PR sits idle re-runs no checks).
The idempotent silent skip stays for identical content (hotfix without
component changes). AGENTS.md documents the enforcement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* ci: Version gate compares strictly, not equality-only
Codex round: a manifest version BEHIND the released stable (stale tree
or bad merge resurrecting an old version) passed the equality check and
printed "ahead", deferring the failure to release time. sort -V
ordering now yields ahead/equal/behind verdicts with distinct failure
messages; only strictly-ahead passes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* fix: Fall back to the legacy walk on YAML-bearing installs
Patch76 review: the YAML exclusion made the component path's default
call shape strictly narrower than the legacy walk it replaces — the
legacy walk reads YAML dashboard bodies (as the fuzzy and
include_config routes still do), so a YAML-bearing install got a
permanently-partial response missing YAML-body matches. yaml_skipped>0
now discards the component frame and runs the legacy walk: coverage is
identical to pre-component behaviour regardless of path, the false
partial disappears, and storage-only installs keep the single-frame
win. The yaml partial fragment and its threading are gone (the signal
became the fallback trigger). Also fixes the fetch_dashboards_list
docstring that claimed the component list serves storage-only rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
* chore: empty commit to re-dispatch GitHub's default-setup CodeQL checks
The Analyze pair died at runner level during a GitHub Actions incident
and dynamic runs cannot be re-run or re-requested for an existing SHA.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DxGQhYBQg7681uCdr9w9qB
---------
Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
echo "version gate ok: pending ${VER} is strictly ahead of released ${stable}" ;;
84
+
equal)
85
+
echo "::error::This PR changes custom_components/ha_mcp_tools but keeps the component version at ${VER}, which is already released as the mirror's latest stable - merged this way the changes never ship (the stable tag step skips existing tags; dev pre-release tags sort below the equal stable). Master is level with stable: bump manifest.json + const.py (and the parity-test literal) once to open the next pending version, per AGENTS.md 'Version bumps ride the stable release cycle'."
86
+
exit 1 ;;
87
+
behind)
88
+
echo "::error::This PR changes custom_components/ha_mcp_tools with version ${VER}, which is BEHIND the mirror's released stable ${stable} - a stale tree or bad merge resurrected an old version. Restore the version to lead the released stable (per AGENTS.md 'Version bumps ride the stable release cycle')."
# Stranded-changes backstop (issue #2008 review follow-up): the
272
+
# idempotent skip is harmless ONLY when the already-tagged content
273
+
# matches the current snapshot (a hotfix without component
274
+
# changes). If master took component changes WITHOUT a version
275
+
# bump after v${VER} went stable — e.g. a PR opened while the
276
+
# version was still pending merged after the stable cut — a
277
+
# silent skip here strands them: no mirror release ever carries
278
+
# them, and v${VER}-dev.N pre-release tags sort BELOW the equal
279
+
# stable tag so even pre-release users never see them. Fail loud
280
+
# with the bump instruction instead. Tag-fetch failure keeps the
281
+
# old silent skip (a network blip must not fail the release) but
282
+
# says so.
283
+
if ! git -C /tmp/mirror fetch --quiet --depth=1 origin "refs/tags/v${VER}:refs/tags/v${VER}"; then
284
+
echo "::warning::mirror tag v${VER} exists but could not be fetched for the stranded-changes check - skipping the stable tag as before"
285
+
exit 0
286
+
fi
287
+
if git -C /tmp/mirror diff --quiet "v${VER}" HEAD -- custom_components; then
288
+
echo "mirror tag v${VER} exists and the component snapshot is identical - nothing new to release"
289
+
exit 0
290
+
fi
291
+
echo "::error::mirror tag v${VER} already exists but the current component snapshot DIFFERS from its content - master took component changes without a version bump after v${VER} went stable; skipping would strand them with no installable release. Bump manifest.json + const.py (and the parity-test literal) to open the next pending version per AGENTS.md 'Version bumps ride the stable release cycle', then let the next stable carry the stranded changes."
0 commit comments