From eb2a04480251c4c151b369539adfb451b993ccaf Mon Sep 17 00:00:00 2001 From: Stefano Mioli Date: Sun, 26 Apr 2026 15:19:21 +0200 Subject: [PATCH] docs(claude.md): rewrite in descriptive register, drop directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous CLAUDE.md ran on imperatives ("MANDATORY", "Using grep is always wrong here", "Never invoke without --json", "Never guess fqnames", "Do not read source files"). That register is the same prompt-injection-adjacent shape that was rewritten out of the cli-bridge AGENTS.md: documentation that gets auto-loaded into an agent's context every time it works in the repo, written as if it were issuing runtime instructions to the model. Rewritten as a working reference: what gosymdb does for navigation, how to bootstrap a session, the task-to-command table, fqname resolution, a typical exploration sequence, the common failure modes (unchanged in substance — they were already factual), stale-index recovery, and the convention for keeping this file in sync with code. The Wrong/Right comparison table is gone; the same information is present as a Task/Command table without the coercive framing. The "Using grep ... is always wrong here" header is gone too — the descriptive equivalent ("Grep finds string occurrences; gosymdb finds resolved symbols. The two answer different questions") sits in the navigation section without telling the agent what to do. Net change: -97 / +47 lines. Same content surface, no behavioral directives. --- CLAUDE.md | 144 ++++++++++++++++++------------------------------------ 1 file changed, 47 insertions(+), 97 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f77c990..84ea6f6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,149 +1,99 @@ -# Code Navigation — MANDATORY +# Working on gosymdb -This codebase has gosymdb available. gosymdb is a typed symbol index built on -Go's own analysis toolchain. It resolves symbols the way the compiler resolves -them — not by string matching. +This is the gosymdb repo. The binary built here is the typed Go symbol index, and running it against this codebase is the canonical way to navigate, since the schema and command surface match the source you're editing. -**Using grep, rg, or find for symbol navigation is always wrong here.** -It is slower, noisier, and costs more tokens. The correct tool exists. Use it. +## What gosymdb does for navigation ---- +gosymdb resolves symbols via Go's `go/packages` and `go/types` (the loader and typechecker the compiler uses), not by string matching. For Go-semantic questions ("who calls X", "what implements Y", "what breaks if I change Z") it returns concrete answers with file paths, line numbers, and fully-qualified names. Grep finds string occurrences; gosymdb finds resolved symbols. The two answer different questions, and either is the right tool depending on what's being asked. -## Session start — do this first +## Session-start bootstrap ```bash gosymdb agent-context ``` -This always outputs JSON — the `--json` flag is not needed and has no effect -on this command. Read `env.db` in the response before proceeding. +`agent-context` always emits JSON with the full command reference plus an `env` block (cwd, db path, stale_packages, git state). Reading it once at the top of a session removes the need for `--help` calls later. -**If `env.db` is empty** (no database found in cwd or ancestors): +If `env.db` is empty, no database has been built. The path that fixes this: ```bash gosymdb index --root . --db gosymdb.sqlite -gosymdb agent-context # env.db will now be populated +gosymdb agent-context # env.db now populated ``` -**If `env.db` is non-empty**, auto-discovery is working. All subsequent commands -will find the database automatically — no explicit `--db` needed unless you are -working with a non-default path. +After that, subsequent commands auto-discover the database from cwd or its ancestors; an explicit `--db` flag is only needed for non-default paths. ---- +## Navigation tasks and the corresponding command -## Navigation tasks and the correct tool +| Task | Command | +|------|---------| +| Where is this symbol defined? | `gosymdb def FuncName --json` | +| What calls this function? | `gosymdb callers --symbol --json` | +| What does this function call? | `gosymdb callees --symbol --json` | +| What's in this file? | `gosymdb find --file --json` | +| What's in this package? | `gosymdb find --pkg --json` | +| List all symbols in the DB | `gosymdb find --json` (up to `--limit`) | +| What implements this interface? | `gosymdb implementors --iface --json` | +| What breaks if I change this? | `gosymdb blast-radius --symbol --json` | +| List all packages | `gosymdb packages --json` | +| Index quality report | `gosymdb health --json` | +| Dead code candidates | `gosymdb dead --pkg --json` | +| Where is this type used? | `gosymdb references --symbol --json` | -| What you want | Wrong | Right | -|---------------|-------|-------| -| Where is this symbol defined? | `grep -r "FuncName" .` | `gosymdb def FuncName --json` | -| What calls this function? | `grep -r "FuncName" .` | `gosymdb callers --symbol --json` | -| What does this function call? | Read the entire file | `gosymdb callees --symbol --json` | -| What's in this file? | Read the entire file | `gosymdb find --file --json` | -| What's in this package? | Read every file | `gosymdb find --pkg --json` | -| List all symbols in the DB | — | `gosymdb find --json` (up to `--limit`) | -| What implements this interface? | `grep -r "interface" .` | `gosymdb implementors --iface --json` | -| What breaks if I change this? | Guess | `gosymdb blast-radius --symbol --json` | -| List all packages | — | `gosymdb packages --json` | -| Index quality report | — | `gosymdb health --json` | -| Dead code candidates | — | `gosymdb dead --pkg --json` | -| Where is this type used? | `grep -r "TypeName" .` | `gosymdb references --symbol --json` | +`--json` is supported on every query command; the JSON shape is the contract, while the text output is informal and unstable. `agent-context` emits JSON unconditionally and ignores the flag. -The `--json` flag is mandatory on all commands except `agent-context`. -Never invoke gosymdb query commands without it. +## Resolving fqnames ---- - -## Getting exact fqnames - -Most commands require a fully-qualified name (fqname). Always resolve first: +Most commands take a fully-qualified name (`pkg/path.Symbol` or `pkg/path.*Type.Method`). Two reliable ways to obtain one: ```bash -gosymdb find --q --json # find candidates; returns fqname, kind, file, line -gosymdb def --json # exact single-symbol lookup +gosymdb find --q --json # candidates with their fqname, kind, file, line +gosymdb def --json # exact single-symbol lookup ``` -Use the `fqname` field from the output as `--symbol` in subsequent commands. -Never guess fqnames. Never construct them by hand. +The `fqname` field from those responses is what other commands accept as `--symbol`. Hand-constructed fqnames frequently miss the package path or method-receiver formatting, so the find/def output is the reliable source. ---- - -## Workflow for understanding an unfamiliar symbol +## A typical exploration sequence ```bash -# 1. Find it +# 1. Find candidates by name. gosymdb find --q MyThing --json -# 2. Get the exact definition +# 2. Pin down a specific symbol. gosymdb def MyThing --json -# 3. Who calls it? What does it call? +# 3. Look outward (callers) and inward (callees). gosymdb callers --symbol --json gosymdb callees --symbol --json -# 4. If it's an interface, find implementors +# 4. If it's an interface, find concrete implementors. gosymdb implementors --iface MyThing --json -# 5. If you're about to change it, check blast radius first +# 5. Before changing it, blast radius surfaces the transitive callers. gosymdb blast-radius --symbol --depth 5 --json ``` -Do not read source files to answer questions gosymdb can answer. - ---- +For symbol-shaped questions, this sequence is faster and more accurate than reading source files; for code-shape, control-flow, or comment-context questions, source reading remains the right tool. ## Common failure modes -**`callers` returns 0 for an interface method:** -Interface dispatch is not recorded as call edges — only direct calls are. -Run `implementors --iface ` to find concrete types, then `callers` on -the concrete method. The `hint` field in the callers response will say this. +**`callers` returns 0 for an interface method.** Interface dispatch isn't recorded as a call edge — only direct calls are. The `hint` field in the response flags this when relevant. The recovery is `implementors --iface ` to find concrete types, then `callers` against the concrete method. -**`error_code: no_database`:** -Auto-discovery found nothing. Either `cd` to the project root (where -gosymdb.sqlite lives) or build the index: `gosymdb index --root . --db gosymdb.sqlite`. +**`error_code: no_database`.** Auto-discovery found no SQLite file in cwd or its ancestors. Either `cd` into the project root (where `gosymdb.sqlite` lives) or build the index with `gosymdb index --root . --db gosymdb.sqlite`. -**`error_code: unknown_command_or_flag`:** -The response includes a `valid_subcommands` list. You may be hallucinating a -command name. Run `agent-context` to get the current authoritative command list. +**`error_code: unknown_command_or_flag`.** The response carries a `valid_subcommands` list. `agent-context` returns the current authoritative command surface, which catches drift between memory and the installed binary. -**`dead` reports a symbol you know is called:** -It may be called via interface dispatch (no call edge recorded), `reflect`, -goroutine/defer indirection, or `go:linkname`. The `note` field in the response -explains this. Verify with `callers` before removing anything. +**`dead` reports a symbol you know is called.** It may be reached via interface dispatch (no call edge recorded), `reflect`, goroutine/defer indirection, or `go:linkname`. The `note` field in the response explains. Running `callers` against the symbol confirms reachability before any deletion. -**`find` or `callers` returns too many results:** -Add `--pkg ` to restrict to the relevant package. Use `packages --json` -to discover the right prefix. +**`find` or `callers` returns too many results.** `--pkg ` restricts to a package prefix. `packages --json` lists the available prefixes if the right one isn't obvious. ---- +## Stale-index detection -## Stale index - -If `env.stale_packages` is non-empty in any gosymdb response, the index is -behind the current source. Either rebuild: - -```bash -gosymdb index --root . --force -``` +Every JSON response carries `env.stale_packages`. When non-empty, the indexed source has drifted from disk. Two recovery shapes: -Or pass `--auto-reindex` to any query command to reindex on demand: - -```bash -gosymdb callers --symbol Foo --auto-reindex --json -``` - -`--auto-reindex` uses a git fast-path when available (microseconds) and falls -back to file-hash comparison. Safe to pass routinely. - ---- +- Rebuild explicitly: `gosymdb index --root . --force`. +- Pass `--auto-reindex` on a query, which runs a stale check (git fast-path with file-hash fallback, sub-second on Kubernetes-scale repos) and reindexes affected modules in place before the query runs. Cheap enough to pass on every call. ## Keeping this file current -If you encounter friction that gosymdb can address but this file does not -cover, **add it here**. Specifically: - -- New failure modes → **Common failure modes** section. -- New navigation patterns → **Navigation tasks** table. -- Changes to gosymdb's API (new commands, renamed flags) → reflect them here. - -The goal is that future agents never rediscover what you already learned. +If a failure mode comes up that this file doesn't already describe, adding it under "Common failure modes" with the response field that signals it keeps the doc reliable. New navigation patterns belong in the table above. Renamed flags or new commands should land here at the same time the change lands in code, since this file is the contract this repo expects from itself.