hermes - ✅(Solved) Fix [Bug]: Redundancy in fuzzy match: indentation_flexible (strategy 4) is a strict subset of line_trimmed (strategy 2) [2 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#14497Fetched 2026-04-24 06:16:54
View on GitHub
Comments
2
Participants
2
Timeline
8
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×3labeled ×3commented ×2

Root Cause

No example exists where strategy 4 succeeds and strategy 2 fails, because lstrip is strictly less aggressive than strip and both sides are normalized identically.

Fix Action

Fix / Workaround

Since strip() is a superset of lstrip() — it removes leading whitespace and trailing whitespace — any content that matches after lstrip() normalization will also match after strip() normalization. But not vice versa (e.g., a trailing-space difference is resolved by strategy 2 but missed by strategy 4).

PR fix notes

PR #14522: fix: reorder fuzzy match strategies so indentation_flexible is not dead code

Description (problem / solution / changelog)

What

Reorder the fuzzy matching strategy chain in tools/fuzzy_match.py so that indentation_flexible (strategy 2, uses lstrip()) runs before line_trimmed (strategy 4, uses strip()).

Why

In the previous ordering, line_trimmed was tried before indentation_flexible. Since str.strip() is a strict superset of str.lstrip() (it removes both leading and trailing whitespace, while lstrip only removes leading), any content that matched after lstrip normalization would also match after strip normalization.

This made indentation_flexible dead code — it could never contribute a match because line_trimmed would always match first (or fail, in which case indentation_flexible was guaranteed to fail too).

How

Swapped the order so:

#StrategyNormalizationCatch zone
2indentation_flexiblelstrip()Indentation-only differences (preserves trailing whitespace precision)
4line_trimmedstrip()Indentation + trailing whitespace differences (more aggressive fallback)

Now each strategy has a unique contribution to the matching chain.

Also updated the module docstring to reflect the correct 9-strategy count (was listed as 8, missing unicode_normalized).

Before vs After

# Before: indentation_flexible was never reached
content = "    def foo():\n        pass"
pattern = "def foo():\n    pass"
# line_trimmed matches (strip handles both leading+trailing)
# indentation_flexible is dead code

# After: indentation_flexible matches first (more precise)
# Same content/pattern now matches via indentation_flexible
# line_trimmed still available as fallback for trailing whitespace diffs

Testing

  • All 16 existing tests pass ✓
  • Added 3 new tests in TestIndentationFlexibleOrdering:
    • test_indentation_flexible_used_for_leading_whitespace_only — verifies strategy is now reachable
    • test_line_trimmed_still_catches_trailing_whitespace — verifies fallback still works
    • test_indentation_flexible_not_dead_code — verifies unique contribution with tab indentation

Closes #14497

Changed files

  • tests/tools/test_fuzzy_match.py (modified, +41/-0)
  • tools/fuzzy_match.py (modified, +15/-14)

PR #14777: fix(tools): reorder fuzzy match strategies so indentation_flexible is no longer dead code

Description (problem / solution / changelog)

What does this PR do?

The indentation_flexible strategy in fuzzy_match.py was positioned after a catch-all strategy that always matched first, making it unreachable dead code. Reorders the strategy list so more specific strategies are evaluated before generic fallbacks.

Related Issue

Fixes #14781

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • tools/fuzzy_match.py: Reordered strategy list so specific strategies precede generic fallbacks
  • tests/tools/test_fuzzy_match.py: Tests for strategy ordering and match priority

How to Test

python -m pytest -o 'addopts=' tests/tools/test_fuzzy_match.py -v

Result: 36 passed.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs
  • My PR contains only changes related to this fix
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes
  • I've tested on my platform: macOS 15 (Darwin 24.6.0), Python 3.14.2

Documentation & Housekeeping

  • N/A for all documentation items

Changed files

  • tests/tools/test_fuzzy_match.py (modified, +38/-0)
  • tools/fuzzy_match.py (modified, +13/-7)
RAW_BUFFERClick to expand / collapse

Problem

In tools/fuzzy_match.py, the matching strategies are tried in order, first-match-wins. Strategy 2 (line_trimmed) normalizes both sides with line.strip(), while strategy 4 (indentation_flexible) normalizes with line.lstrip().

Since strip() is a superset of lstrip() — it removes leading whitespace and trailing whitespace — any content that matches after lstrip() normalization will also match after strip() normalization. But not vice versa (e.g., a trailing-space difference is resolved by strategy 2 but missed by strategy 4).

In other words: if strategy 2 fails, strategy 4 is guaranteed to fail as well. It currently has zero chance of contributing a match.

Example

pattern: "hello " # trailing spaces content: "hello" # no trailing spaces

Strategy 2: strip() both → "hello" == "hello" ✓ Strategy 4: lstrip() both → "hello " != "hello" ✗

No example exists where strategy 4 succeeds and strategy 2 fails, because lstrip is strictly less aggressive than strip and both sides are normalized identically.

Suggested fix

Two options:

  1. Remove strategy 4 entirely — it's dead code in the current ordering.
  2. Swap the order — put indentation_flexible before line_trimmed. This way, lstrip() runs first (preserving trailing whitespace precision), and strip() acts as a more aggressive fallback if it fails. This gives each strategy a unique "catch zone":
    • lstrip catches: indentation-only differences
    • strip catches: indentation + trailing whitespace differences

extent analysis

TL;DR

Swap the order of strategies in tools/fuzzy_match.py to put indentation_flexible before line_trimmed to give each strategy a unique catch zone.

Guidance

  • Identify the current order of matching strategies in tools/fuzzy_match.py and verify that line_trimmed is indeed tried before indentation_flexible.
  • Consider the implications of removing strategy 4 entirely, as it is currently dead code in the given ordering.
  • To implement the suggested fix, update the order of strategies to prioritize indentation_flexible over line_trimmed.
  • Test the updated implementation with example patterns and content to ensure the desired matching behavior.

Example

# Before
strategies = [line_trimmed, ..., indentation_flexible]

# After
strategies = [indentation_flexible, ..., line_trimmed]

Notes

This fix assumes that the goal is to utilize both lstrip and strip normalization strategies effectively. If the intention is to prioritize one over the other, the order can be adjusted accordingly.

Recommendation

Apply workaround: Swap the order of strategies to prioritize indentation_flexible over line_trimmed, allowing each strategy to contribute unique matches. This approach ensures that both normalization strategies are utilized effectively.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING