"Small cleanup" of a helper twenty features use
对二十个功能共用的工具函数做了“小清理”
While working in one feature, the agent follows an import into a shared utility and refactors it, changing behavior for every caller.
Applies to: Claude Code · Cursor · Codex · Copilot agent
Symptom
The task touched `login.ts`; the diff also rewrote `utils/helpers.ts`. Unrelated features start misbehaving after merge.
Why it happens
Night Forge describes "helpful clean-up on shared utilities" as a recurring pattern from Cursor forum reports. The agent sees old style in a "watering-hole" file and has no sense of its blast radius.
Example
From the Night Forge write-up.
textAsked: change src/features/auth/login.ts
Agent: also refactored src/utils/helpers.ts (imported by ~20 features)
Copy-paste fixes
Pick the files for the agents you use. Snippets are in English so they work in any repo.
Claude Code: add to CLAUDE.md
CLAUDE.md## Shared code is off-limits by default
- Do not modify shared modules (`utils/`, `lib/`, `shared/`, `common/`, design-system components)
unless the task is about them.
- If the fix truly requires a shared change, stop, list the callers (`rg -l "from .*helpers"`), and ask.
- Prefer a local change in the feature over editing a shared helper.
Codex, Copilot coding agent and others: add to AGENTS.md
AGENTS.md## Shared code is off-limits by default
- Do not modify shared modules (`utils/`, `lib/`, `shared/`, `common/`, design-system components)
unless the task is about them.
- If the fix truly requires a shared change, stop, list the callers (`rg -l "from .*helpers"`), and ask.
- Prefer a local change in the feature over editing a shared helper.
Cursor: project rule
.cursor/rules/shared-helper-cleanup.mdc---
description: "AgentFails: 'Small cleanup' of a helper twenty features use"
alwaysApply: true
---
## Shared code is off-limits by default
- Do not modify shared modules (`utils/`, `lib/`, `shared/`, `common/`, design-system components)
unless the task is about them.
- If the fix truly requires a shared change, stop, list the callers (`rg -l "from .*helpers"`), and ask.
- Prefer a local change in the feature over editing a shared helper.
Require review on shared code (CODEOWNERS)
.github/CODEOWNERS# Any PR (human or agent) touching shared code needs a maintainer's review
/src/utils/ @your-org/maintainers
/src/lib/ @your-org/maintainers
/src/shared/ @your-org/maintainers
Claude Code PreToolUse hook: block edits outside the declared scope
Adapted from the diff-budget design on dev.to (yureki_lab). Put allowed globs, one per line, in .agent-scope at the repo root. Exit code 2 blocks the tool call and feeds stderr back to the agent. It only covers file-edit tools; shell writes still need the diff check.
.claude/hooks/scope_guard.py#!/usr/bin/env python3
"""PreToolUse hook: block Edit/Write/MultiEdit outside the globs in .agent-scope."""
import fnmatch, json, os, sys
call = json.load(sys.stdin)
if call.get("tool_name") not in ("Edit", "Write", "MultiEdit"):
sys.exit(0)
root = os.environ.get("CLAUDE_PROJECT_DIR") or call.get("cwd") or os.getcwd()
scope_file = os.path.join(root, ".agent-scope")
if not os.path.exists(scope_file):
sys.exit(0) # no scope declared for this task -> allow
allowed = [l.strip() for l in open(scope_file) if l.strip() and not l.startswith("#")]
target = os.path.relpath(call["tool_input"]["file_path"], root)
if any(fnmatch.fnmatch(target, pat) for pat in allowed):
sys.exit(0)
print(
f"BLOCKED: {target} is outside this task's scope ({', '.join(allowed)}).\n"
"If this edit is required, stop and ask the user to widen .agent-scope, with a one-line reason.\n"
"If it is just an improvement you noticed, add it to FOLLOWUPS.md and keep going.",
file=sys.stderr,
)
sys.exit(2)