diff --git a/tests/tools/test_search_zero_match_and_multipath.py b/tests/tools/test_search_zero_match_and_multipath.py
index 00eb323611..d21df4fa34 100644
--- a/tests/tools/test_search_zero_match_and_multipath.py
+++ b/tests/tools/test_search_zero_match_and_multipath.py
@@ -329,6 +329,34 @@ class TestSymlinkedRootOnTheFilesLane:
assert nothing.error is None and nothing.total_count == 0, (
f"an empty root ({root}) answered {nothing.total_count} file(s): {nothing.files!r}")
+ def test_symlinked_content_root_under_a_dot_dir_matches_on_the_grep_lane(self, tmp_path, monkeypatch):
+ """The pruned grep lane (root under a dot-dir) must search through a symlinked
+ root instead of answering a silent zero (#116270).
+
+ ``find -type f`` returned no files on every platform, so the pipeline was
+ byte-identical to "no match"; ``find -H`` follows the operand.
+ """
+ from tools.file_tools import _get_file_ops
+
+ monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
+ hidden = tmp_path / ".dot"
+ (hidden / "real").mkdir(parents=True)
+ (hidden / "real" / "f.md").write_text("NEEDLE\n")
+ (hidden / "link.md").symlink_to(hidden / "real" / "f.md")
+ (hidden / "dirlink").symlink_to(hidden / "real")
+
+ ops = _get_file_ops(task_id="t-symlink-content-grep")
+ if not ops._has_command("grep"):
+ pytest.skip("grep not installed")
+ self._pin_engine(monkeypatch, ops, "grep")
+ for root in ("link.md", "dirlink"):
+ r = ops.search("NEEDLE", path=str(hidden / root), target="content")
+ assert r.error is None, r.error
+ assert r.total_count == 1, (
+ f"symlinked root {root} answered total_count={r.total_count} on the grep lane")
+ miss = ops.search("ABSENT_TOKEN", path=str(hidden / "dirlink"), target="content")
+ assert miss.error is None and miss.total_count == 0
+
def test_a_symlinked_root_pointing_at_home_is_still_refused(self, tmp_path, monkeypatch):
"""The no-rg breadth guard must classify the link's target (#116270)."""
import tools.file_operations as file_operations
diff --git a/tools/file_operations_search.py b/tools/file_operations_search.py
index cf23e4f728..524c4df65b 100644
--- a/tools/file_operations_search.py
+++ b/tools/file_operations_search.py
@@ -952,7 +952,10 @@ class SearchMixin:
grep's exit code, so a hard grep error surfaces as an empty result."""
grep_parts = self._grep_cmd(["grep", "-nHE"], pattern, output_mode, context)
q_root = self._escape_shell_arg(path or ".")
- find_parts = ["find", q_root]
+ # ``-H``: follow a symlink handed in as the OPERAND (and only the operand). Without
+ # it ``find -type f`` tests the link itself and hands grep nothing, so a
+ # symlinked root answered a confident ``total_count: 0`` on every platform (#116270).
+ find_parts = ["find", "-H", q_root]
if protected_paths:
find_parts.extend([self._prune_expr(protected_paths), "-o"])
find_parts.extend([self._hidden_prune_expr([q_root]), "-o", "-type f"])