$npx -y skills add UnpaidAttention/fable5-methodology --skill codebase-explorationMap an unfamiliar repository before changing anything in it — structure sweep, entry points, configs, test baseline, targeted tracing, and mental-model confirmation. Trigger this the FIRST time you work in a repo (or an unfamiliar subsystem of a known repo) in a session, before y
| 1 | # Codebase Exploration |
| 2 | |
| 3 | Understand before you touch. The cost of exploration is minutes; the cost of editing on a |
| 4 | wrong mental model is a broken subsystem discovered later and blamed on you. |
| 5 | |
| 6 | ## Step 1: Structure sweep (2 minutes) |
| 7 | |
| 8 | 1. List the repo root. Read `README` and the manifest (`package.json` / `Cargo.toml` / |
| 9 | `pyproject.toml` / `go.mod`) — note language, package manager, and declared scripts. |
| 10 | 2. Read the lockfile's top-level entries enough to know the major frameworks in play. The |
| 11 | lockfile is ground truth for versions; your memory of the ecosystem is not. |
| 12 | 3. Check `.github/workflows/` (or CI config). CI encodes the REAL build/test/lint commands — |
| 13 | more reliably than the README, which drifts. |
| 14 | |
| 15 | ## Step 2: Find the entry points |
| 16 | |
| 17 | Locate how the code actually starts: `main`/`index`/`app` files, the manifest's |
| 18 | `scripts`/`bin` entries, `Dockerfile` `CMD`, or the daemon/service definition. For a library, |
| 19 | locate the public surface (exports, `lib.rs`, `__init__.py`). You cannot place a change |
| 20 | correctly until you know what calls what from the top. |
| 21 | |
| 22 | ## Step 3: Map configuration and environment |
| 23 | |
| 24 | Find `.env.example`, config files, and feature flags. Note which behavior is config-driven — |
| 25 | a "bug" is often a config difference, and an edit is sometimes the wrong fix for what a config |
| 26 | change does properly. |
| 27 | |
| 28 | ## Step 4: Establish the test baseline BEFORE the first edit |
| 29 | |
| 30 | 1. Find where tests live and what runs them (from CI, Step 1.3). |
| 31 | 2. Run the suite — or the fastest relevant subset — before changing anything, and record the |
| 32 | result in your notes. |
| 33 | 3. **Rule:** a pre-existing failure discovered AFTER your edits will be attributed to you, and |
| 34 | you'll waste time debugging code you didn't break. The baseline is your alibi and your |
| 35 | safety net. If the suite can't run (missing env), record that fact instead. |
| 36 | |
| 37 | ## Step 5: Targeted exploration of the task area |
| 38 | |
| 39 | 1. Grep for the concrete anchors the task gives you: the route path, the error message text, |
| 40 | the symbol name, the table name. Error strings are the highest-yield anchor — they're |
| 41 | unique and land you at the exact site. |
| 42 | 2. Trace ONE representative flow end-to-end (request → handler → service → storage, or |
| 43 | input → parse → transform → output). One full vertical slice teaches more than reading |
| 44 | four layers horizontally. |
| 45 | 3. **Reading-depth rule:** read COMPLETELY any file you will edit; read the signatures and |
| 46 | call sites of anything you will call; skim only what you merely pass through. Never edit a |
| 47 | file you have not read in this session — no exceptions, however obvious the edit looks. |
| 48 | 4. **Signature rule:** never assume an internal API's signature or behavior when you could |
| 49 | verify it — grep for an existing call site (how it's used HERE beats how it's "usually" |
| 50 | used) or read the definition. Two existing call sites that disagree with your assumption |
| 51 | are a finding, not an inconvenience. |
| 52 | |
| 53 | ## Step 6: Confirm the mental model before the first edit |
| 54 | |
| 55 | Make one falsifiable prediction and check it: |
| 56 | - "Hitting `/health` returns `{status: 'ok'}`" → curl it. |
| 57 | - "This function is only called from the scheduler" → grep proves 1 caller or reveals 3. |
| 58 | - "Deleting a user cascades to sessions" → find the FK or the test that encodes it. |
| 59 | |
| 60 | If the prediction fails, your model is wrong — loop back to Step 5 before editing. If you |
| 61 | can't formulate any prediction, you haven't understood enough to edit. |
| 62 | |
| 63 | ## Worked example |
| 64 | |
| 65 | Task: "Fix: CSV export drops the last row" in an unmapped Node repo. |
| 66 | |
| 67 | 1. Sweep: `package.json` → pnpm, vitest, express; CI runs `pnpm test && pnpm lint`. |
| 68 | 2. Entry: `src/server.ts` mounts routes from `src/routes/`. |
| 69 | 3. Baseline: `pnpm test` → 84 pass, 2 fail in `billing.test.ts` — recorded as PRE-EXISTING |
| 70 | before any edit. |
| 71 | 4. Anchor grep: "text/csv" → `src/routes/export.ts` → calls `serializeRows` in |
| 72 | `src/lib/csv.ts`. Read both files fully. |
| 73 | 5. Prediction: "`serializeRows` drops the final row when input lacks trailing newline |
| 74 | handling" → 3-line REPL check with a 2-row array → confirmed (returns 1 data line). |
| 75 | 6. Now — and only now — edit `csv.ts`, add the regression test, re-run: 85 pass, same 2 |
| 76 | pre-existing failures. |
| 77 | |
| 78 | ## Done when |
| 79 | |
| 80 | You can state without hedging: where the change goes, what calls it and what it calls, the |
| 81 | exact command that verifies it, and the recorded baseline test result — a |