Skip to content

docs(internal): correct the reference-graph ranking comment - #2285

Merged
Patch76 merged 4 commits into
homeassistant-ai:masterfrom
Patch76:docs/graph-ranking-comment
Aug 27, 2026
Merged

docs(internal): correct the reference-graph ranking comment#2285
Patch76 merged 4 commits into
homeassistant-ai:masterfrom
Patch76:docs/graph-ranking-comment

Conversation

@Patch76

@Patch76 Patch76 commented Aug 26, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Corrects two pieces of prose in smart_search that state something the code does not do — one # comment and one docstring. No behaviour change; the AST is identical with docstrings stripped.

_GRAPH_HIT_SCORE's ranking claim. It says fuzzy scores are capped at 100, so a confirmed reference ranks at or above every fuzzy name match. The cap it describes is in _search_in_dict and bounds the config-body score. The fuzzy name score comes from _calculate_entity_score, which has a single return and is not capped at 100.

This is not the common case — most unrelated names score well under 100 — and it does not need to be. One name echoing the queried entity's words is enough, and you can check this one yourself: _calculate_entity_score("automation.kitchen_lights_evening", "Kitchen Lights Evening", "automation", "light.kitchen_ceiling") returns 118, against a graph hit's fixed 100. That is sufficient to push the confirmed reference below a config that merely resembles the query. The exact-mode half of the original claim holds and stays.

The replacement comment is scoped to what I could verify: it speaks about graph-only hits, because _merge_graph_hits leaves an already-scored record's score alone, and it hedges the "still counted" clause, because _scrub_results_for_enforce runs after the merge and can drop a graph hit outright under visibility enforcement.

_individual_fetch_budgeted's deprioritize docstring names the automation and script callers; the scene branch passes the set too.

This documents the fuzzy ordering rather than changing it. If you meant the stronger guarantee the comment originally claimed, that needs match_in_references in the sort key in _paginate_and_build_response. Note that also touches exact mode, where graph hits tie with body hits at 100 and follow them within a bucket — across buckets the flattening order in _paginate_and_build_response dominates, not the append order. That is a behaviour change, so it stayed out of this comment correction.

One negative result worth recording, since it is about the code this comment describes. I built and then dropped a change that skipped the deprioritization when include_config=True, on the theory that a caller asking for bodies wants the confirmed reference's body most. Measuring it killed the idea. In the case I ran — two automations, a budget affording one fetch — the current ordering returned two matches (a templated reference with its body, plus the graph hit) and the change returned one, because the unfetched config scores 0 and falls below threshold. Which id wins the budget is incidental to the change rather than caused by it, and the caller is told the scan was partial either way, so the reference goes unnamed rather than undisclosed. What the change does remove is the guarantee that non-graph ids get the budget first. Losing a reference is worse than returning one without its body, so the ordering as it stands is right. That experiment is not in this branch — treat it as an anecdote, not evidence.

Type of change

  • 🐛 Bug fix
  • ✨ New feature
  • 📚 Documentation
  • 🔧 Maintenance/refactor
  • 🧪 Tests only
  • 💥 Breaking change

Testing

Nothing to test — prose only. tests/src/unit/test_search_related_graph.py collects 41 and passes 41 with --maxfail=0. CI covers the rest.

  • I have tested these changes with a LLM agent
  • All automated tests pass (uv run pytest)
  • Code follows style guidelines (uv run ruff check)

Checklist

  • I have updated documentation if needed

Summary by CodeRabbit

  • Documentation
    • Clarified budget-ordering guidance to include scene callers alongside automations and scripts.
    • Expanded fuzzy-search ranking documentation, including graph-only match scoring and visibility behavior.

_GRAPH_HIT_SCORE said fuzzy scores are capped at 100, so a confirmed reference
ranks at or above every fuzzy name match. The cap lives in _search_in_dict and
bounds the config-body score. The fuzzy name score comes from
_calculate_entity_score, which accumulates its bonuses with no ceiling, so a
name echoing the queried entity's words can pass 100 on its own and outrank a
graph hit's fixed 100.

The exact-mode half of the claim holds and stays. The comment now states what
fuzzy mode does, scoped to graph-ONLY hits (_merge_graph_hits leaves an
already-scored record's score alone) and to the case where visibility
enforcement does not scrub the record.

Also name the scene caller in _individual_fetch_budgeted's deprioritize
paragraph: it passes the set too, alongside automation and script.
@ghhamcp

ghhamcp commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

@codex review — apply the review criteria in .gemini/styleguide.md in addition to AGENTS.md guidance

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: e9b82440b6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 76de9044-4e55-4562-a066-b361f824617d

📥 Commits

Reviewing files that changed from the base of the PR and between be94263 and d54ae3b.

📒 Files selected for processing (1)
  • src/ha_mcp/tools/smart_search/_graph.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ha_mcp/tools/smart_search/_graph.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change updates smart-search documentation. It adds scene callers to the budget-ordering description and clarifies fuzzy-search ranking, score behavior, and total_matches handling. Runtime behavior is unchanged.

Changes

Smart search documentation

Layer / File(s) Summary
Search behavior documentation
src/ha_mcp/tools/smart_search/_fetch.py, src/ha_mcp/tools/smart_search/_graph.py
The documentation includes scene callers and clarifies that graph-only matches scored at 100 can rank below fuzzy name matches above 100. It also documents total_matches handling and visibility enforcement.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Merge Risk: ⚪ Minimal · up to d54ae

This change only corrects comments and docstring wording without changing runtime behavior, so no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: correcting the internal reference-graph ranking comment. It is concise and relevant.
Description check ✅ Passed The description explains both documentation corrections, states that behavior is unchanged, identifies the testing performed, and completes the relevant template sections. The unchecked broader test a…
Full details: Description check

Explanation

The description explains both documentation corrections, states that behavior is unchanged, identifies the testing performed, and completes the relevant template sections. The unchecked broader test and lint items are acceptable for this documentation-only change.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The comment said no value of _GRAPH_HIT_SCORE makes fuzzy mode
symmetric, and rested that on _calculate_entity_score having no
ceiling. The absent clamp does not carry the claim: that score is a sum
of bounded parts, so a large enough constant would dominate it. What
actually rules such a constant out is the exact-mode property the
paragraph above asserts - every survivor scores 100 there, and raising
the constant breaks that tie. Name the condition instead of leaving it
implicit.
@Patch76

Patch76 commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

Choices Made:

The comment on _GRAPH_HIT_SCORE claimed fuzzy scores are capped at 100, so a confirmed reference ranks at or above every fuzzy name match. Two ways to close the gap between that sentence and the code: change the sentence, or change the ranking. I changed the sentence. Making the guarantee real means putting match_in_references into the sort key in _paginate_and_build_response, which also reorders exact mode — a behaviour change, and not one I wanted to fold into a comment correction. The PR body records what that change would take, so the option stays open.

The replacement speaks about graph-only hits because _GRAPH_HIT_SCORE is the constant those hits carry: _merge_graph_hits sets match_in_references on a record that was already scored and leaves the score alone, so the constant governs only the records the merge itself creates. That is a scoping choice about what this comment is responsible for, not a claim that the rest ranks safely — _paginate_and_build_response sorts on score alone, nothing reads match_in_references for ranking, and a reference found both ways is exposed to the same reordering. The "still counted" clause is hedged because _scrub_results_for_enforce runs after the graph merge and can drop a graph hit outright under visibility enforcement. The exact-mode half of the original comment holds and stays.

The _individual_fetch_budgeted docstring named the automation and script callers. The scene path in _scenes.py passes the set too, so I added it.

Problems Encountered:

The original claim was plausible because a 100 cap does exist — it just belongs to a different scorer. _search_in_dict caps its fuzzy return at 100, and that is the config-body score. The name score comes from _calculate_entity_score in utils/fuzzy_search.py, which accumulates exact, partial, weighted-ratio, room-keyword and device-keyword contributions into one running total and returns it without capping it at 100. Reading _search_in_dict on its own reproduces the wrong conclusion; only tracing both shows they are separate paths feeding one max().

My first replacement traded one overstatement for another. It said no value of the constant makes fuzzy mode symmetric, and rested that on the missing clamp — but a sum of bounded parts is itself bounded, so a large enough constant would dominate the name score. What actually rules such a value out is the exact-mode tie asserted one paragraph above: _graph_record gives every graph hit this constant, and raising it would stop those hits scoring 100 alongside body hits there. The comment now names that condition instead of leaving it implicit.

I also built and then dropped a change to the code this comment describes: skipping the deprioritization when include_config=True, on the theory that a caller asking for bodies wants the confirmed reference's body most. It gives the budget to the wrong ids. A confirmed reference is a match with or without its body, whereas an id whose match lives inside its config is a match only if the config is actually read — an unfetched config scores 0, the record falls back on its name score, and below the fuzzy threshold it is not returned at all. Deprioritizing spends the budget where it can still change the answer, so the ordering as it stands is right. That experiment is not in this branch.

The code comment carried this one round longer than the reasoning did: it still said the name score accumulates "without a ceiling" after the paragraph above had already recorded that a sum of bounded parts is bounded. The same loose wording was in this summary's own description of _calculate_entity_score, and I have corrected it above. The comment now says the score is not capped at 100, which is the part the surrounding argument uses.

@Patch76
Patch76 marked this pull request as ready for review August 27, 2026 11:02
@Patch76
Patch76 requested review from a team and kingpanther13 August 27, 2026 11:02
@ghhamcp

ghhamcp commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

@codex review — apply the review criteria in .gemini/styleguide.md in addition to AGENTS.md guidance

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: be94263058

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kingpanther13

Copy link
Copy Markdown
Member

Merging master in for you to clear the dead ci. I cannot officially review till tonight or tomorrow though

@kingpanther13 kingpanther13 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One accuracy correction is needed in src/ha_mcp/tools/smart_search/_graph.py.

The new comment says _calculate_entity_score “accumulates without a ceiling.” That is not accurate: the function is a finite sum of bounded 0–100 similarity values and fixed bonuses. The intended distinction is that its result is not capped at 100.

Please change this to “is not capped at 100” (or equivalent). The rest of the diff is accurate.

…unded

The comment described `_calculate_entity_score` as accumulating "without a
ceiling". The function is a finite sum of bounded terms — three
`calculate_*_ratio` helpers that each return 0-100, plus fixed exact/partial
and keyword bonuses — so a bound does exist. What matters for the surrounding
argument is only that the bound is not 100, which is what the sentence now
says. The rest of the comment is unchanged.
@Patch76

Patch76 commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

Fixed in d54ae3b. The comment now says _calculate_entity_score is not capped at 100, rather than that it accumulates without a ceiling.

You are right about the shape of the function. Every term is either a fixed bonus or the single weighted term int(0.7 * max(...) + 0.8 * max(...) + 0.6 * domain_ratio) built from calculate_ratio / calculate_partial_ratio / calculate_token_sort_ratio, each of which returns 0-100, so the sum is bounded. What the paragraph actually needs is only that the bound sits above 100, and that is what it claims now. The example from the PR body still holds: _calculate_entity_score("automation.kitchen_lights_evening", "Kitchen Lights Evening", "automation", "light.kitchen_ceiling") returns 118 against a graph hit's fixed 100.

The same claim had leaked into two other places, so I corrected those as well. The PR body said the score accumulates "with no clamp" and the implementation summary said it "returns it with no ceiling"; both now say it is not capped at 100. The code change is the two comment lines and nothing else, so the AST is unchanged. I also appended a note to the implementation summary recording that the comment kept the loose wording after the reasoning there had already ruled it out.

@kingpanther13 kingpanther13 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requested accuracy correction is addressed: the comment now correctly says _calculate_entity_score is not capped at 100, rather than claiming it has no ceiling. I rechecked the full current diff and found no remaining concerns. Approved.

@Patch76
Patch76 merged commit 6defbae into homeassistant-ai:master Aug 27, 2026
40 checks passed
@Patch76
Patch76 deleted the docs/graph-ranking-comment branch August 27, 2026 20:05
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Your changes are now in the dev channel!

Your PR has been merged to master and is available for testing in the dev channel.

Test your changes before the next stable release (biweekly Wednesday):
📖 Dev Channel Documentation

Quick start

# Run dev version
uvx ha-mcp-dev

# Check version
uvx ha-mcp-dev --version

Docker:

docker pull ghcr.io/homeassistant-ai/ha-mcp:dev
docker run --rm -i \
  -v ha-mcp-dev-data:/home/mcpuser/.ha-mcp \
  -e HOMEASSISTANT_URL=http://your-ha:8123 \
  -e HOMEASSISTANT_TOKEN=your_token \
  ghcr.io/homeassistant-ai/ha-mcp:dev

Found an issue? Please open a new bug report and mention this PR for context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants