Map the Blast Radius
You are working on a change to gastownhall/gascity and need to know what it ripples to before you commit. A narrow-looking change can run on a config reload, leak a goroutine in a CLI path, or read stale config — the failures a maintainer's adoption review catches. This skill maps the surface so you catch them first.
Run it from your local checkout. It uses only git, grep, and gh — no
special index required. (If you've run codegraph init in the repo, its
codegraph_callers / codegraph_impact are faster than grep for caller traces;
fall back to grep when it returns nothing or the call is dynamic.)
mainbelow means the upstream main you're targeting —origin/mainif origin isgastownhall/gascity, elseupstream/main.
Step 1 — Enumerate touched functions
git diff main...HEAD --stat
git diff main...HEAD -- '*.go' | grep '^[+-]func ' | grep -v _test.go
Record each new/modified function: package, name, exported/unexported.
Step 2 — Map callers (upward)
For each touched function, find who calls it:
grep -rn 'FunctionName(' cmd/gc/ internal/ --include='*.go' | grep -v _test.go
Classify every caller into an execution context — the context determines the risk:
| Context | Characteristics | Key concern |
|---|---|---|
newCityRuntime |
Runs once at startup | Safe for one-time sweeps |
controller tick |
Runs every ~30s in the reconcile loop | Must be fast and idempotent |
tryReloadConfig / update() |
Runs on config-file change | Reading cs.cfg directly sees the PREVIOUS config |
buildStores(cfg) |
Called from update() before the cs.cfg swap |
Must use the passed cfg, not cs.cfg |
cobra RunE |
CLI one-shot | Process exits after return — goroutines die |
API handler |
Long-lived HTTP server | Fire-and-forget goroutines OK |
buildOrderDispatcher |
Startup AND reload | No one-time operations here |
gracefulStopAll |
Shutdown path | Two-pass: interrupt then force-kill |
Step 3 — Map callees (downward)
Read each modified function body and trace what it calls. Flag:
cs.cfgreads inside anything called fromupdate()→ stale-config risk (B21)store.SetMetadata*/store.Close/store.Create→ errors must propagate (B12)shellCommand/exec.Command→ is the timeout shared with another subsystem? (B13)go func()→ is the caller a CLI path? Then it needs a completion signal (B17)
Step 4 — Config-field impact
If the change adds or modifies a config field, verify it appears in all of these — a missing one is a silent drop:
grep -rn 'FieldName' internal/config/ cmd/gc/ --include='*.go' | grep -v _test.go
config.Agentstructconfig.AgentPatchstructconfig.AgentOverridestructapplyAgentPatch()applyAgentOverride()poolAgents()deep-copy incmd/gc/pool.goApplyAgentDefaults()if there's a city-wide defaultEffective*()accessor if the nil/empty distinction matters (B11)
TestAgentFieldSync catches the struct definitions only — the apply functions
and the pool deep-copy must be checked by hand.
Step 5 — Domain-boundary crossings
Check whether the change crosses any of these boundaries:
| Boundary | Risk |
|---|---|
| Pool agents ↔ named-session agents | Map keys leak; scale checks count both? (B18) |
| User agents ↔ infrastructure agents | Loops applying work config to control_dispatcher (B15) |
| Startup path ↔ reload path | One-time ops in the reload path corrupt in-flight goroutines (B16) |
| CLI path ↔ server path | Goroutine lifecycle differs (B17) |
| Config snapshot ↔ live config | update()-chain functions read stale cs.cfg (B21) |
| Reconciler tick ↔ background goroutines | Shared semaphores/timeouts with unintended scope (B13) |
Step 6 — Concurrency
If the change involves goroutines, semaphores, or shared state:
git diff main...HEAD -- '*.go' | grep -E 'go func|\.Lock\(|\.RLock\(|<-|chan '
- New goroutine in a CLI path → does it return a completion signal? (B17)
- New semaphore → does it bound exactly the right set of operations? (B13)
- Package-level mutable state written by reload →
atomic.*/ mutex? (B30) - Shared state guarded by the right mutex (e.g.
serviceStateMu)?
Step 7 — Cross-repo impact
Gas City extracts subsystems from gastownhall/gastown, depends on
gastownhall/beads, and uses dolthub/dolt as its storage engine. If your
change touches a contract that spans repos, check it:
- Beads interface — if you touch bead-store operations (
Create,SetMetadata,Query,Close), confirm againstgastownhall/beadsthat the interface contract still holds. Browse the repo on GitHub orgh search code --repo gastownhall/beads '<symbol>'. - Gastown original behavior — for extracted code, check how
gastownhall/gastownhandled the same path. Classify a divergence as[extraction bug](gascity broke something gastown gets right),[inherited](gastown has the same limitation), or[intentional divergence](document why). - Upstream drift — has gastown/beads/dolt changed recently in this area?
gh search commits --repo gastownhall/gastown '<area-keyword>'. Flag if gascity may need to pull in an upstream fix.
Step 8 — Report
Blast Radius — <branch>
Changed functions:
<pkg>.<Func> — <one-line summary>
Execution contexts:
<Func> called from: startup / tick / reload / CLI / API / shutdown
Upward callers:
<Func> ← <caller1> ← <caller2> (context: reload path)
Downward callees:
<Func> → store.SetMetadata (must propagate error)
<Func> → go func() (CLI path — needs completion gate)
Config field chain: complete / MISSING <which step>
Domain boundary crossings:
Pool ↔ named-session : safe / RISK: <detail>
User ↔ infra agents : safe / RISK: <detail>
Startup ↔ reload : safe / RISK: <detail>
CLI ↔ server : safe / RISK: <detail>
Snapshot ↔ live config : safe / RISK: <detail>
Tick ↔ background : safe / RISK: <detail>
Concurrency:
Goroutines: none / N spawned, completion signals: yes/no
Semaphores: none / scoped to: X subsystem only
Shared state: none / protected by: <mutex>
Cross-repo:
Beads interface : compatible / DIVERGES: <detail>
Gastown original: matches / [extraction bug] / [inherited] / [intentional divergence]
Upstream drift : none recent / NEEDS SYNC: <detail>
Risk summary:
HIGH: <anything a maintainer would call a "major">
MED: <anything a maintainer would call a "minor">
LOW: <nits, style>
Recommendation: PROCEED / FIX BEFORE COMMIT
Keep findings to one line each, with a file:line ref. The HIGH/MED items feed
the Risks section of your plan-implementation
plan and the audit in review.
When to use
This skill is dual-use — a phase of a parent skill AND a standalone utility:
- As Phase 2 of
plan-implementation, before you write code. - Standalone, for ad-hoc impact mapping — before committing a change that touches the reconciler, controller, or lifecycle code; when a change spans multiple subsystems (config + runtime, beads + dispatch); when you're unsure whether a function runs on startup only or also on reload; when scoping a refactor or mapping a change for someone else.