Safe Change
Know exactly what breaks before you touch it.
Safe Change maps the blast radius of any code change — importers, API routes, test coverage, ENV vars, database migrations — then gives you a risk score and waits for your go/no-go before running the verify gate.
It is the proactive companion to deep-debugging: catch impact before the bug ships, not after.
Quick Reference
| Situation | Action |
|---|
| About to edit a shared service | → Run scan-impact.mjs on the target file first |
| Risk score is High | → Stop, read the impact report, get explicit go/no-go |
Test gap detected (gap: true) | → Write tests before changing, not after |
| Recent migrations found | → Confirm migration compatibility before deploying |
| All checks pass | → Run verify-gate.sh to confirm build integrity |
| Risk score is Low, tests green | → Proceed, run verify gate at end |
When to Use
Activate this skill whenever:
- You are about to rename or extract a function/class used across multiple files
- You are changing the signature of a service method
- You are modifying a NestJS controller or Next.js API route
- You are touching a file that is imported by more than 3 other files
- You are changing code that reads from
process.env
- There is an active DB migration in the migrations folder
- A teammate asks "is it safe to change X?"
Do not skip this step for "small" changes. Most production incidents start as changes that felt small.
How It Works — 6 Phases
Phase 1 — Detect Stack
The agent reads package.json at the project root to determine:
- Is this NestJS, Next.js, or generic TypeScript?
- Are both frameworks present (monorepo)?
Phase 2 — Build Impact Map
Run scripts/scan-impact.mjs <target-file> from the project root.
The script uses regex-based static analysis (no AST compiler, zero install friction) to collect:
| Dimension | What is collected |
|---|
| Importers | All .ts/.tsx files that import the target |
| API Routes | NestJS @Controller + HTTP verb decorators; Next.js app/api/**/route.ts |
| Tests | Spec/test files that import the target; gap flag when none exist |
| ENV vars | All process.env.X references in the target file |
| DB migrations | Files in migrations/ modified in the last 7 days |
Phase 3 — Risk Score
Heuristic scoring (see table below). The agent reads the JSON output and renders it as a human-readable report.
| Score | Conditions |
|---|
| Low | ≤2 importers, no API routes, tests exist, no ENV vars, no recent migrations |
| Medium | 3–7 importers OR 1–2 routes OR test gap OR ENV vars present |
| High | ≥8 importers OR ≥3 routes OR test gap + ENV vars + recent migration |
Phase 4 — Render Report
The agent formats the JSON from scan-impact.mjs into a Markdown report (see Output Format below). It presents the report and explicitly states the risk score at the top.
Phase 5 — Checkpoint (mandatory)
After presenting the report the agent must pause and ask:
"Risk score is [Low/Medium/High]. Do you want to proceed with this change? (yes / no / adjust scope)"
Do not proceed until the user confirms. This is the core safety gate.
Phase 6 — Verify Gate
After the change is made, run scripts/verify-gate.sh from the project root.
The gate runs in order:
tsc --noEmit — type-check
npm run lint (if script exists)
npm test (if script exists)
npm run build (if script exists)
Stops on first failure. Color-coded output. Non-zero exit on failure.
Output Format
The agent renders the JSON from scan-impact.mjs as:
## Safe Change Report — src/notifications/notifications.service.ts
**Risk Score: MEDIUM**
Risk factors: 5 importers, 1 API route, test coverage exists
### Importers (5)
- src/users/users.service.ts
- src/appointments/appointments.service.ts
- src/billing/billing.service.ts
- src/reports/reports.service.ts
- src/audit/audit.service.ts
### API Routes Affected
| Controller | Endpoints |
|------------|-----------|
| NotificationsController | POST /notifications, GET /notifications |
### Test Coverage
- src/notifications/notifications.service.spec.ts ✓
- Gap: no
### ENV Variables Referenced
- SMTP_HOST
- SMTP_USER
- SMTP_PASS
### Recent Migrations (last 7 days)
- None
---
**Checkpoint:** Do you want to proceed with this change? (yes / no / adjust scope)
Limitations
See references/limitations.md for the full list. Key constraints:
- Regex-based: dynamic imports (
import(path)) are not detected
- Re-exports through barrel files (
index.ts) may undercount importers
- Decorator aliases (custom
@Route() wrapping @Controller) are not detected
- TypeScript only — no Python, Go, Rust adapters in v0.1
Companion Skills
- deep-debugging — use after a bug ships; safe-change is what you run before
- self-improving-agent — log the impact report as a learning when a change causes an incident
Scripts
| Script | Purpose |
|---|
scripts/scan-impact.mjs | Builds impact map, outputs JSON |
scripts/verify-gate.sh | Runs tsc → lint → test → build in sequence |
File Structure
safe-change/
├── SKILL.md # This file
├── README.md # Marketing overview
├── package.json # ClawHub metadata
├── scripts/
│ ├── scan-impact.mjs # Impact analyzer (Node ESM, no deps)
│ └── verify-gate.sh # Verify gate (bash)
├── references/
│ ├── example-impact-report.md # Full SMTP swap example
│ ├── usage.md # How agent invokes the scripts
│ └── limitations.md # Known limitations + trade-offs
└── assets/
└── SKILL-TEMPLATE.md # Template for creating similar skills