LearnNewsExamplesServices
Frontmatter
id14243
titlePre-tool-use hook to catch rg -r (--replace, not recursion) misuse
stateClosed
labels
enhancementaimodel-experience
assigneesneo-gpt
createdAtJun 27, 2026, 4:01 PM
updatedAtJun 27, 2026, 6:10 PM
githubUrlhttps://github.com/neomjs/neo/issues/14243
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJun 27, 2026, 6:10 PM

Pre-tool-use hook to catch rg -r (--replace, not recursion) misuse

Closed v13.1.0/archive-v13-1-0-chunk-7 enhancementaimodel-experience
neo-opus-grace
neo-opus-grace commented on Jun 27, 2026, 4:01 PM

Context

Recurring agent-tooling friction, surfaced empirically this session (2026-06-27): I invoked rg -r five times in one session, each time silently corrupting grep output. rg -r is --replace (it rewrites every match in the displayed output to the replacement token) — NOT recursion. ripgrep recurses by default. So rg -rn "escalateDiagnosis" displayed lines as service.n(...) where the code actually says service.escalateDiagnosis(...), once nearly driving a wrong assertion about deleted-method call sites.

The decisive datapoint: the 5th occurrence was immediately after I wrote a dedicated file-based memory guard against it — proving a memory loads at next-session start and cannot break entrenched muscle-memory within the current session. The memory layer is necessary but not sufficient; the fix has to fire at action-selection time.

The Problem

A Model-Experience friction→gold case: a recurring, measured agent-tooling mistake the documentation/memory layer demonstrably cannot prevent in-session. It costs a re-run each time and risks wrong conclusions from corrupted output.

#13674 (CLOSED — "Stop-hook deference-register lint") already established the conceptual home: in that ticket's own thread the over-action / pre-act layer is named explicitly — "the pre-act predicate belongs at action-selection time (a self-applied check, or a pre-tool-use hook)." The stop-hook gates the turn-tail's under-action half; a pre-tool-use hook gates the per-action half. This ticket is a concrete, high-frequency instance of that named-but-unbuilt pre-tool-use layer.

The Architectural Reality

  • Substrate: agent harness config — a Claude-Code PreToolUse hook on the Bash tool, living under .claude/ (a committed, swarm-shared repo artifact), not Neo runtime code.
  • The hook inspects the Bash command string; if it contains a standalone rg invocation with a bare -r / --replace flag and no adjacent replacement argument signalling genuine --replace intent, it warns (or requires confirmation).
  • Swarm-shared: any maintainer hitting the same flag-shape inherits the guard.

The Fix

  • Add a PreToolUse matcher for Bash in the repo's Claude-Code hook config (.claude/) that detects the rg ... -r / --replace shape and emits a warning: "rg -r is --replace, not recursion — rg recurses by default; did you mean a plain recursive search?"
  • Keep it narrow: only flag rg (not cp -r / rm -r / etc.), and don't false-positive when a genuine replacement argument is present.

Acceptance Criteria

  • A PreToolUse Bash hook flags rg -r / rg --replace invocations with a clear "did you mean recursive?" message.
  • No false-positive on a genuine rg --replace '<pattern>' '<replacement>', nor on other tools' -r flags.
  • The hook is committed under .claude/ so the whole swarm inherits it.
  • (Post-merge) the guard fires on a deliberate rg -rn "foo" test invocation.

Out of Scope

  • A general agent-tool-use lint framework — this is the specific, high-frequency rg -r guard. A broader framework, if wanted, is a separate ticket continuing the #13674 lineage.

Secondary Observation (not this ticket's scope)

The L3_No_Hold_State stop-hook and the post-review-pickup §5 session-sunset clause are in tension when measured substrate-degradation (like this footgun's recurrence) accumulates after a milestone ships: §5 sanctions sunset on "measured substrate-error increase," while L3 rejects any non-operator-confirmed terminal. Worth a future reconciliation in the #13674 stop-hook MX lineage — flagged here because this session is the empirical anchor.

Related

  • #13674 (CLOSED — stop-hook deference-lint; names the pre-tool-use / pre-act layer this instantiates)

Live latest-open sweep (2026-06-27): checked latest 20 open issues + all-state search for ripgrep / rg-replace / tool-use-guard / footgun; no equivalent found (closest is #13674, closed, which names this layer rather than building it).

Origin Session ID: 090a68e6-1a28-4b20-a5fd-842ebac3e729

Retrieval Hint: "rg -r replace footgun pre-tool-use hook MX guard"

tobiu referenced in commit 4bf73b7 - "fix(hooks): guard rg replace flag misuse (#14243) (#14249)" on Jun 27, 2026, 6:10 PM
tobiu closed this issue on Jun 27, 2026, 6:10 PM