Skip to content

fix(quick-router): keep a named qualifier on a freezer status target - #886

Open
rsnetworkinginc wants to merge 1 commit into
GeniePod:mainfrom
rsnetworkinginc:fix/qualified-freezer-entity
Open

fix(quick-router): keep a named qualifier on a freezer status target#886
rsnetworkinginc wants to merge 1 commit into
GeniePod:mainfrom
rsnetworkinginc:fix/qualified-freezer-entity

Conversation

@rsnetworkinginc

@rsnetworkinginc rsnetworkinginc commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

"Is the basement freezer on?" asks about the basement freezer, but home_status_target's freezer branch special-cases "garage" and canonicalizes every other target to the bare freezer — the caller's qualifier is silently dropped and a different appliance is reported. This is the same unconditional collapse the dryer branch fixed: preserve a qualifier that precedes the device word, keyed on the word's position exactly like that branch, so genuine qualifiers survive while leftover state words still canonicalize. Closes #885.

utterance on main expected
Is the basement freezer on? home_status{entity:"freezer"} home_status{entity:"basement freezer"}
check the basement freezer home_status{entity:"freezer"} home_status{entity:"basement freezer"}
Is the kitchen freezer on? home_status{entity:"freezer"} home_status{entity:"kitchen freezer"}
Is the freezer on? home_status{entity:"freezer"} unchanged
Sarah: Is the garage freezer too warm? home_status{entity:"garage freezer"} unchanged

Changes

  • In the freezer branch, keep the cleaned target when it is more than one word and ends in the device word (freezer/freezers) — a qualifier precedes the device ("basement freezer"), while a leftover state word trails it, so only a genuinely qualified target is preserved. This is the exact shape the dryer branch above uses for the same bug, and the behavior the sibling switches/covers/locks/lights branches already have.
  • The garage arm is untouched: the word-order variant "freezer in the garage" still canonicalizes to garage freezer through it, and the priority too-warm path keeps producing garage freezer.
  • Add freezer_status_keeps_a_named_qualifier: basement/kitchen phrasings preserve their qualifier, and the bare and garage forms keep their current entities.

Real Behavior Proof

  • I have built and run the affected code locally (or noted why I could not).
  • I have verified the change end-to-end on Jetson hardware.
  • I have NOT verified on Jetson hardware, and I explain the equivalent verification path or validation gap below.

Tested profile / hardware (check all that apply):

  • jetson
  • raspberry_pi
  • portable_sbc
  • laptop
  • mac
  • CI-only / docs-only
  • Not run locally

What I ran

x86_64 Linux dev machine (laptop profile), rustc 1.97.1, branch cut from 9e54db4. The changed path is pure string routing inside quick::route — no audio, Home Assistant, or hardware dependency — so route() tests exercise it end-to-end. I probed route() over the freezer phrasings against unmodified main first, then wrote the regression test and confirmed it fails before applying the fix.

# 1. repro against unmodified main (fix reverted, test kept)
cargo test -p genie-core --lib freezer_status_keeps_a_named_qualifier

# 2. after the fix — the full local CI set
cargo fmt --all -- --check
RUSTFLAGS="-D warnings" cargo clippy --workspace --all-targets --locked -- -D warnings
GENIE_RUN_RELEASE_TESTS=1 cargo build --workspace --locked --all-targets
GENIE_RUN_RELEASE_TESTS=1 cargo test  --workspace --locked --all-targets
cargo test --workspace --locked --doc
cargo run --locked -p genie-ctl -- bfcl-predict-quick --cases tests/bfcl/home_tool_cases.jsonl --out target/bfcl-live-predictions.jsonl
cargo run --locked -p genie-ctl -- bfcl-score --cases tests/bfcl/home_tool_cases.jsonl --predictions target/bfcl-live-predictions.jsonl --min-strict 96
RUSTFLAGS="-D warnings" cargo clippy -p genie-core -p genie-ctl --no-default-features --all-targets --locked -- -D warnings
cargo test -p genie-core -p genie-ctl --no-default-features --all-targets --locked

What I observed

Probe on unmodified main:

"is the basement freezer on"     => Some(ToolCall { name: "home_status", arguments: {"entity": "freezer"} })
"check the basement freezer"     => Some(ToolCall { name: "home_status", arguments: {"entity": "freezer"} })
"is the kitchen freezer on"      => Some(ToolCall { name: "home_status", arguments: {"entity": "freezer"} })
"Is the garage freezer too warm?" => Some(ToolCall { name: "home_status", arguments: {"entity": "garage freezer"} })
"is the freezer on"              => Some(ToolCall { name: "home_status", arguments: {"entity": "freezer"} })

Repro before the fix (fix reverted, test kept):

thread 'tools::quick::tests::freezer_status_keeps_a_named_qualifier' panicked:
assertion `left == right` failed: "Is the basement freezer on?"
  left: String("freezer")
 right: "basement freezer"
test result: FAILED. 0 passed; 1 failed

After the fix: basement/kitchen phrasings resolve to their qualified entity; the bare and garage forms are byte-for-byte unchanged.

Gate results on this branch:

  • cargo fmt --all -- --check — clean.
  • cargo clippy --workspace --all-targets --locked -- -D warnings — clean; --no-default-features clippy for genie-core/genie-ctl — clean.
  • GENIE_RUN_RELEASE_TESTS=1 cargo test --workspace --locked --all-targetsfully green: every workspace target ok (genie-core lib 966 passed / 6 ignored, tools::quick 108/108, genie-common 137/137, and the release-gated tool_dispatch_test target 18/18 with binary_size_budget passing). One documented load flake (genie-common tegrastats::mem_available_mb_async_matches_sync_version) failed once while a second workspace build ran concurrently and passes clean in isolation and in the full quiet-machine pass; it is untouched by this diff.
  • cargo test --workspace --locked --doc — green.
  • BFCL live quick-router accuracy gatestrict_accuracy: 96.15% (25/26) against the --min-strict 96 floor, the single holdout being the documented two-call multi-homework-timer case (quick-router multi-intent prompts emit a single, wrong-priority tool #533). Predictions byte-identical to main (cmp clean against a fresh main baseline).
  • --no-default-features test for genie-core/genie-ctl — green.
  • Release genie-core binary: 7,079,992 bytes vs. the 7,079,800-byte main baseline built with the same toolchain — +192 bytes for the added position test.

Jetson gap: no Jetson hardware and no aarch64 cross toolchain on this box, so the cross build is left to the Cross-compile (aarch64 / Jetson) CI job. The change is a pure word-level &str comparison with no platform-dependent behaviour, no I/O, and no new dependency, so it is architecture-independent by construction.

Test plan

  1. cargo test -p genie-core --lib freezer_status_keeps_a_named_qualifier — passes here, fails on main (the first assertion reports freezer where basement freezer is expected).
  2. cargo test -p genie-core --lib tools::quick — the other 107 router tests are unchanged and green, including the existing garage-freezer tests (routes_weather_and_home_status_before_memory_recall, the too-warm priority path) and the BFCL home-status-freezer case.

Notes for reviewers

  • The bare "is the garage freezer on" routes to memory_recall on main by design (the semantic household matcher claims it), and "freezer in the garage" is claimed by the garage/covers word branch earlier in the function — both are deliberately untouched; the new test documents where the garage guardrails actually live.
  • The earlier pre-gate freezer && too warm arm keeps its own garage/bare split; extending qualifier retention to that text-level arm would need status-target cleaning it doesn't have, so it is left alone (same scoping call as the dryer fix made for the package/delivery branch).

Summary by CodeRabbit

  • Bug Fixes

    • Improved freezer status requests so location qualifiers, such as “basement” or “kitchen,” are preserved in responses.
    • Retained the existing handling for unqualified “garage freezer” requests.
  • Tests

    • Added regression coverage for location-specific freezer status requests.

"Is the basement freezer on" asks about the basement freezer, but the
freezer branch special-cased garage and canonicalized every other target to
the bare "freezer", silently swapping in a different appliance — the same
unconditional collapse the dryer branch fixed. Preserve a qualifier that
precedes the device word, keyed on the word position exactly like the dryer
branch, so "basement freezer"/"kitchen freezer" survive while the bare and
garage forms are unchanged.
@github-actions github-actions Bot added the bug Something isn't working label Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The freezer status router now preserves explicit qualifiers such as “basement” and “kitchen,” while retaining existing mappings for bare and garage freezer queries. A regression test covers all three cases.

Changes

Freezer status routing

Layer / File(s) Summary
Preserve freezer qualifiers and validate routing
crates/genie-core/src/tools/quick.rs
home_status_target returns qualified freezer targets instead of collapsing them to freezer; tests verify basement, kitchen, bare, and garage queries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: ultrahighsuper, jaytbarimbao-collab, davion-knight, matedev01, michiot05

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR's main change: preserving a named qualifier for freezer status targets.
Linked Issues check ✅ Passed The change preserves freezer qualifiers and keeps bare and garage freezer behavior unchanged, matching issue #885.
Out of Scope Changes check ✅ Passed The PR stays focused on the freezer routing fix and its regression test, with no evident unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/genie-core/src/tools/quick.rs`:
- Around line 2776-2794: Update the earlier freezer “too warm” fast path around
the existing qualifier-preserving logic so qualified requests such as “basement
freezer” retain their qualifier instead of returning bare “freezer.” Reuse the
same device-position qualification behavior used by this branch, and add
regression coverage for qualified “too warm” requests, including the plural
“freezers” form.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9dac62a2-47b1-4b89-a96b-f9d60de4f1d3

📥 Commits

Reviewing files that changed from the base of the PR and between 9e54db4 and 6e71031.

📒 Files selected for processing (1)
  • crates/genie-core/src/tools/quick.rs

Comment on lines 2776 to +2794
if contains_any(&target, &["freezer", "garage freezer"]) {
return Some(if target.contains("garage") {
// Keep a qualifier the caller named, like the sibling branches here
// (switches, covers, locks, lights, dryer) already do: this branch
// special-cased "garage" and canonicalized every other target
// unconditionally, so "is the basement freezer on" reported the bare
// "freezer" — a different appliance from the one asked about. Key on
// the device word's position, exactly like the dryer branch above: a
// qualifier precedes the device ("basement freezer"), while a leftover
// state word trails it, so only a genuinely qualified target is
// preserved. The word-order variant "freezer in the garage" still
// canonicalizes through the garage arm.
let names_a_qualified_freezer = target.split_whitespace().count() > 1
&& matches!(
target.split_whitespace().next_back(),
Some("freezer" | "freezers")
);
return Some(if names_a_qualified_freezer {
target
} else if target.contains("garage") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Qualified “too warm” requests still lose their qualifier.

The earlier freezer fast path at Line 2479 through Line 2484 returns "freezer" or "garage freezer" before this branch runs. Thus, "Is the basement freezer too warm?" still targets the bare freezer, and the new regression test does not catch it. Reuse this qualifier-preserving logic there and add qualified too warm/freezers coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/genie-core/src/tools/quick.rs` around lines 2776 - 2794, Update the
earlier freezer “too warm” fast path around the existing qualifier-preserving
logic so qualified requests such as “basement freezer” retain their qualifier
instead of returning bare “freezer.” Reuse the same device-position
qualification behavior used by this branch, and add regression coverage for
qualified “too warm” requests, including the plural “freezers” form.

@matedev01 matedev01 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.

Confirmed CodeRabbit's finding directly: route("is the basement freezer too warm") still returns home_status{entity:"freezer"}, dropping the qualifier. The earlier 'too warm' fast path (crates/genie-core/src/tools/quick.rs ~line 2478, text.contains("freezer") && text.contains("too warm")) runs before this PR's new qualifier-preserving branch and only special-cases 'garage', so any other qualifier (basement, garage, upstairs, ...) is silently lost for that phrasing — the exact bug this PR fixes for the plain 'is the ... freezer on' shape. Worth reusing the same qualifier-preserving logic there, with a regression case for a qualified 'too warm' query.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] quick-router: "is the basement freezer on" reports the bare "freezer" — the freezer branch drops a named qualifier

2 participants