One morning, with a handful of wallpaper-app crash reports waiting, I noticed I was re-reading the same old bug notes again and again. As an indie developer, most of my triage time goes to confirming "I've seen this before."
So I fixed the order of investigation: grep a known-bug table first, follow the ID to its detail, and record a new bug only when nothing matches. When I hand a crash investigation to Gemini CLI, I give it that same order.
This article covers how I build the table and what I put in (and keep out of) GEMINI.md. The routine is something I run myself while maintaining Dolice's apps. I have not measured time or token savings inside Gemini CLI, so I can't give you numbers.
One bug, one line
Alongside the detailed write-ups, I keep a search-only table. Five columns are enough:
BW-009 | android | crashes at launch only in release | needed resources stripped by shrinking | add a keep rule | detail:bugs/BW-009.md
BW-014 | ios | full-screen ad stops after navigating back | static holder keeps a destroyed screen | refresh the reference only | detail:bugs/BW-014.md(These lines illustrate the format; swap in your own IDs and symptoms.)
Keeping it to one line means a grep hit can match on either the symptom wording or the cause wording, and the hit can be pasted straight into a conversation. Details live in separate files that you open only when needed.
Three steps to look something up
- Pull two or three terms from the log (exception name, class name, symptom words) and grep them.
- Open the detail file for the ID that matched.
- Only if nothing matches, assign a new ID and add one line to the table.
Step 3 includes the write-back on purpose. If investigations don't return to the table, the same re-reading happens next time.
How to add a new row
When an investigation ends in no hit, I add a provisional row even before the cause is confirmed, with a trailing ?. Once confirmed, I drop the ? and fix the cause column. If I waited for confirmation, similar reports arriving in the meantime would be counted as new. IDs are just a prefix and a running number, with no meaningful words, so I never have to renumber when my theory of the cause changes.
A grep helper you can paste in
# Put this in ~/.zshrc or similar. Change the path to your own table.
BUGS="$HOME/work/known-bugs.txt"
bugfind() {
# Multiple arguments are ANDed. Case-insensitive.
[ $# -eq 0 ] && { echo "usage: bugfind <word> [word...]" >&2; return 2; }
local out; out=$(cat "$BUGS")
for w in "$@"; do out=$(printf '%s\n' "$out" | grep -iF -- "$w"); done
if [ -z "$out" ]; then
echo "NO_HIT: record this as a new bug" >&2
return 1
fi
printf '%s\n' "$out"
}Two gotchas. grep -F treats the term as a fixed string, so terms with symbols like Foo.bar() work as-is. And returning 1 on no hit lets you branch later: "no hit means go to the new-record step."
Put an index and rules in GEMINI.md, not the table
Gemini CLI gathers GEMINI.md files from several places, concatenates them, and sends them with your prompts. The documentation lists three sources: ~/.gemini/GEMINI.md in your home directory, the current directory and its parents up to the project root, and subdirectories below the working directory. /memory show prints the full concatenated context (Gemini CLI docs).
So I assume anything in GEMINI.md rides along with every question. I don't put the table there. I write only a short procedure:
## Crash investigation procedure
- Before guessing at a cause, grep `known-bugs.txt` (the `bugfind` function, or `grep -iF`).
- Read only the detail file for the matching ID. Do not read the whole table or everything under bugs/.
- Only when nothing matches, propose a new ID and output the line to append to the table.You can split files with the @file.md syntax, but I expect the imported content to be loaded all the same. Handing over a "search it" procedure rather than a large table should keep the conversation lighter, though I haven't measured that either.
What worked, and what didn't
What worked: the entry point to every investigation became the same. Whoever (or whatever) investigates starts with the table, so known bugs are less often mistaken for new ones.
What didn't: varying symptom wording. The same cause is reported as "crashes," "freezes," or "goes blank." I added alternative phrasings to the symptom column, which helps but doesn't fully solve it. I also added one rule: on no hit, retry once with a nearby term before recording a new bug.
Trying it, in order
- Turn your past bugs into one-line rows (the latest ten are enough to start).
- Add
bugfindand use it once on a real report. - Add the three-line procedure to
GEMINI.mdand check how it loads with/memory show. - Any investigation that ended in no hit goes back into the table that same day.
Trimming tools is a separate lever from trimming standing instructions, and both matter. For the tool side, see how I decide MCP tool include/exclude; for the basics, the Gemini CLI guide.