Chapter 5

Separate Research From Editing

On an unknown codebase the agent's first result should be a solution map, not a patch. A plan or read-only mode lowers the risk of fixing the symptom in the wrong layer and creates a checkable basis for changes. In the companion project this is literally a separate step: first a bounded repository index, and only then a write.

TypeScript
export async function indexRepository(rootInput: string, options: IndexOptions): Promise<RepositoryMap> {
  if (options.maxFiles < 1 || options.maxBytesPerFile < 0) {
    throw new Error("Index limits must be positive");
  }
  const root = await canonicalRoot(rootInput);
  const entries: RepositoryEntry[] = [];
  let totalBytes = 0;
  let truncated = false;

  async function visit(directory: string): Promise<void> {
    const children = await readdir(directory, { withFileTypes: true });
    children.sort((left, right) => left.name.localeCompare(right.name));
    for (const child of children) {
      if (entries.length >= options.maxFiles) {
        truncated = true;
        return;
      }
      if (ignoredNames.has(child.name)) {
        continue;
      }
      const absolutePath = join(directory, child.name);
      if (child.isSymbolicLink()) {
        continue;
      }
      if (child.isDirectory()) {
        await visit(absolutePath);
        if (truncated) {
          return;
        }
        continue;
      }
      if (!child.isFile()) {
        continue;
      }
      await resolveExistingContained(root, absolutePath);
      const fileStats = await stat(absolutePath);
      const relativePath = normalizeRelativePath(relative(root, absolutePath));
      entries.push({
        path: relativePath,
        bytes: Math.min(fileStats.size, options.maxBytesPerFile),
        kind: classify(relativePath)
      });
      totalBytes += fileStats.size;
    }
  }

  await visit(root);
  return { root, entries, totalBytes, truncated };

The indexer is deliberately bounded - it doesn't follow symbolic links, sorts deterministically, and stops on the file and byte limits.

Investigate the defect, but don't edit files yet.
Find the entry point, called functions, tests, and local instructions.
Show the actual data path as file:symbol.
Separate observed facts from hypotheses.
At the end, propose the minimal write scope and verification commands.

The research pass should return not a retelling but specifics: the entry point and public contract, the files and symbols actually involved in the behavior, the data path from input to effect, existing tests and the nearest analog, verification commands, and the uncertainties that change the design. The request for such a mode is stated product-independently: "investigate the defect, but don't edit files yet; show the actual data path as file:symbol; separate observed facts from hypotheses; at the end propose the minimal write scope."

And a caveat that saves time.

A small task needs no ceremony. The official Claude Code guidance explicitly notes the overhead of planning. For an obvious typo or a one-line replacement, an exact prompt and a diff check are enough. A plan is needed when the solution path or scope isn't obvious.

Research gave a map. The next step is a plan you can reject before a single patch exists.

Knowledge check

Why should the first result on an unfamiliar codebase be a map, not a patch?

Links