Skip to content

fix(tools): scale a digit followed by a spelled magnitude in parse_spoken_number - #838

Closed
jeffrey701 wants to merge 1 commit into
GeniePod:mainfrom
jeffrey701:fix/spoken-number-digit-magnitude
Closed

fix(tools): scale a digit followed by a spelled magnitude in parse_spoken_number#838
jeffrey701 wants to merge 1 commit into
GeniePod:mainfrom
jeffrey701:fix/spoken-number-digit-magnitude

Conversation

@jeffrey701

@jeffrey701 jeffrey701 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

parse_spoken_number has a digit fast-path that returns a leading digit token immediately, silently dropping a spelled magnitude that follows it:

if let Some(Ok(value)) = tokens.get(start).map(|token| token.parse::<u64>()) {
    return Some((value, start + 1));   // "5 hundred" -> (5, 1), "hundred" lost
}

So parse_spoken_number(&["5", "hundred"], 0) returns (5, 1) instead of (500, 2). This produces a confidently-wrong calculator answer: route("what is 20 percent of 5 hundred") builds calc_number_starting_at from parse_spoken_number, gets 5, and emits "5 * 20 / 100"1, instead of "500 * 20 / 100"100. The duration path is affected the same way ("5 hundred seconds" → a 100s timer, not 500).

Only the mixed digit + spelled-magnitude form was broken — the fully-spelled ("five hundred" → 500) and fully-digit ("500") forms already work, and "five hundred" is exactly what "5 hundred" means. This seeds the accumulator from the leading digit when a magnitude follows, so the existing hundred/thousand loop scales it; a bare digit with no magnitude still returns immediately, unchanged.

(Note: parse_amount has a separate first-digit shortcut with the same blind spot on the temperature-setpoint path; that is a distinct function and left for a follow-up so this change stays focused on parse_spoken_number.)

Closes #837

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

genie-core is a Linux-only crate, so it doesn't compile on my Windows dev box — CI runs the real cargo test on Linux, exercising the four added tests. Locally I reproduced parse_spoken_number (old vs new) plus cardinal_word in a rustc harness.

What I ran

rustc --edition 2021 pr_nw_test.rs && ./pr_nw_test

What I observed

PR-A OK: OLD '5 hundred'->5 (dropped magnitude); NEW ->500; no regression.

The harness asserts OLD ["5","hundred"](5,1) (the dropped magnitude), NEW → (500,2), ["3","thousand"](3000,2), ["2","thousand","five","hundred"](2500,4), ["5","hundred","and","twenty"](520,4), and no change for the bare-digit (["5","minutes"]→(5,1), ["42"]→(42,1)) and fully-spelled (["five","hundred"]→(500,2)) forms.

Summary by CodeRabbit

  • Bug Fixes

    • Improved spoken-number parsing for expressions such as “5 hundred” and “3 thousand.”
    • Supports continued parsing across mixed digits and spelled-out magnitudes, including optional “and.”
    • Preserves existing behavior for standalone digits and fully spelled-out magnitudes.
  • Tests

    • Added coverage for digit-magnitude scaling and mixed-format number expressions.

…oken_number

The digit fast-path returned a leading digit token immediately, silently
dropping a spelled magnitude after it: parse_spoken_number(["5","hundred"])
returned (5, 1) not (500, 2). So "what is 20 percent of 5 hundred" computed on 5
("5 * 20 / 100" = 1) instead of 500 (= 100), and "5 hundred seconds" made a 100s
timer. Only the mixed digit+magnitude form was wrong; fully-spelled and
fully-digit already worked.

Seed the accumulator from the leading digit when a hundred/thousand follows so
the existing magnitude loop scales it; a bare digit still returns immediately.
Adds tests. (parse_amount has a separate twin shortcut; focused follow-up.)
@github-actions github-actions Bot added the bug Something isn't working label Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

parse_spoken_number now scales a leading digit followed by hundred or thousand, while preserving bare-digit behavior. Tests cover mixed digit and magnitude inputs, optional “and”, and existing spelled-number parsing.

Changes

Spoken number parsing

Layer / File(s) Summary
Scale digit-magnitude inputs
crates/genie-core/src/tools/number_words.rs
parse_spoken_number continues parsing when a leading digit is followed by hundred or thousand, and tests cover scaling, continuation, bare digits, and spelled magnitudes.

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

Possibly related PRs

  • GeniePod/genie-claw#825: Updates the same parser and adds routed calculator coverage for the mixed digit-and-magnitude case.

Suggested labels: bug

🚥 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 states the main fix: scaling a digit before a spelled magnitude in parse_spoken_number.
Linked Issues check ✅ Passed The change addresses the mixed digit+magnitude bug in #837 and preserves the listed existing behaviors.
Out of Scope Changes check ✅ Passed No obvious unrelated changes are indicated; the fix and tests stay within the linked parsing bug scope.
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.

🧹 Nitpick comments (1)
crates/genie-core/src/tools/number_words.rs (1)

125-150: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add calculator and duration regression tests.

These tests validate parse_spoken_number directly, but not its downstream consumers in crates/genie-core/src/tools/calc_input.rs and crates/genie-core/src/tools/quick.rs. Add one regression case through each path to ensure "5 hundred" remains scaled after integration.

🤖 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/number_words.rs` around lines 125 - 150, Add
integration regression tests for the calculator path in calc_input.rs and the
duration path in quick.rs, each exercising the input “5 hundred” and asserting
it produces the scaled value 500. Keep the existing parse_spoken_number unit
tests unchanged and follow the established test helpers and assertions in each
consumer.
🤖 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.

Nitpick comments:
In `@crates/genie-core/src/tools/number_words.rs`:
- Around line 125-150: Add integration regression tests for the calculator path
in calc_input.rs and the duration path in quick.rs, each exercising the input “5
hundred” and asserting it produces the scaled value 500. Keep the existing
parse_spoken_number unit tests unchanged and follow the established test helpers
and assertions in each consumer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 47a636d0-19c9-4354-8e69-c62f23345970

📥 Commits

Reviewing files that changed from the base of the PR and between 7c67906 and 5144a9b.

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

@jeffrey701

Copy link
Copy Markdown
Contributor Author

Closing in favor of #825, which was opened a day earlier and covers the same parse_spoken_number digit-plus-magnitude fix (including the quick.rs routing coverage). Deferring to it to avoid a duplicate.

@jeffrey701 jeffrey701 closed this Jul 24, 2026
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.

parse_spoken_number drops a spelled magnitude after a digit ("5 hundred" parses as 5, not 500)

1 participant