●ASSISTANT — Google Assistant begins shutting down on September 4, with Gemini taking over on Android phones and tablets, Wear OS, and Android Auto●MIGRATION — The rollout is staged and may take several weeks to reach everyone. Once it lands, Assistant stops working and there is no way to switch back●SURFACES — Wear OS and Android Auto are easy to overlook here. Voice control when you cannot look at a screen asks something rather different from talking to a phone at your desk●DROP — The September Android Drop adds a Remembered list in Find Hub for items without a tracker tag, along with Motion Assist and Keep inside Google Messages●CODE — Gemini Advanced now accepts multiple code files in one upload, so you can hand over part of a repository instead of pasting a single file at a time●ROLE — The framing around Gemini keeps shifting from chat tool toward a supervised digital worker that handles files, screens, documents, and code●ASSISTANT — Google Assistant begins shutting down on September 4, with Gemini taking over on Android phones and tablets, Wear OS, and Android Auto●MIGRATION — The rollout is staged and may take several weeks to reach everyone. Once it lands, Assistant stops working and there is no way to switch back●SURFACES — Wear OS and Android Auto are easy to overlook here. Voice control when you cannot look at a screen asks something rather different from talking to a phone at your desk●DROP — The September Android Drop adds a Remembered list in Find Hub for items without a tracker tag, along with Motion Assist and Keep inside Google Messages●CODE — Gemini Advanced now accepts multiple code files in one upload, so you can hand over part of a repository instead of pasting a single file at a time●ROLE — The framing around Gemini keeps shifting from chat tool toward a supervised digital worker that handles files, screens, documents, and code
When Gemini Let Me Upload Several Code Files at Once, I Stopped Selecting by Folder
Once you can hand Gemini a slice of a repository, the hard part moves from pasting to choosing. I measured the dependency graph of my own Next.js repo, found that folder-based selection caught zero callers, and wrote down the selection routine I use now.
The morning I read that several code files could be uploaded together, the first thing I opened was not Gemini. It was the folder tree of my own repository.
Back when I pasted one file at a time, there was almost nothing to decide. Pasting by hand caps you at two or three files, and that narrowness quietly stood in for judgement. The moment I could send a set, the substitute disappeared.
For a while I selected by folder. I wanted to fix something in src/lib, so I handed over src/lib whole. That did not work well. The comments that came back were correct as prose and never once touched the place that actually broke.
I only understood why after I stopped guessing and started counting. Select the files that call the one you want to change, not the neighbours of the one you want to change. It took me three rounds of the same question to arrive at that sentence.
The day pasting turned into choosing
When the attachment limit rises, the difficulty moves. I used to ask whether one file contained everything. Now I ask whether the one file that decides the answer is somewhere inside these ten.
The awkward part is that an answer arrives either way. Within whatever slice you provided, a coherent explanation can usually be assembled. From the reader's side, a missing file and a wrong conclusion look identical — a trap that simply did not exist in the paste-one-file era.
The Lab sites I run are Next.js on Cloudflare Workers, and article retrieval is concentrated in one thin module. That module is always what I want to change. What breaks is always the code calling it.
How many callers a folder actually catches
Rather than argue from feeling, I counted on the real repository: everything under src in Gemini Lab, 68 TypeScript and TSX files, about 450 KB.
I measured two sets. One is "other files in the same folder." The other is "files that import this one" — the callers.
Target file
Other files in same folder
Callers
Callers in that folder
src/lib/content.ts
2
19
0
src/config/pricing.ts
1
9
0
src/lib/premium.ts
2
3
0
src/config/gone-slugs.ts
1
1
0
Across those four targets there are 32 callers, and zero of them live in the target's folder. In this repository, selecting by folder caught not a single caller.
The 19 callers of src/lib/content.ts are spread across 17 separate directories: article listings, tags, level pages, feeds, the sitemap, a search API route. They share exactly one thing, which is that they fetch articles. The more carefully you split folders by role, the further your callers scatter.
That also explains my three rounds. I was forgetting to attach the one file that decided the answer, every single time.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You can list, mechanically, which files break when you touch one file, and defend the attachment set you hand to Gemini
✦You can spot the folder-shaped blind spot that leaves every caller out, before you spend several rounds on confident but irrelevant answers
✦You can decide where to stop expanding the dependency graph before the attachment size doubles on you
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Tracing by hand guarantees omissions, so I wrote a small dependency-graph script — short enough that its output can serve directly as the attachment list.
# deps.py — list the callers and dependencies of one file# usage: python3 deps.py src/lib/content.tsimport os, re, sysROOT = "src"EXTS = (".ts", ".tsx")CANDIDATES = (".ts", ".tsx", "/index.ts", "/index.tsx")files = [ os.path.join(d, f).replace("\\", "/") for d, _, fs in os.walk(ROOT) for f in fs if f.endswith(EXTS)]def resolve(spec, frm): """Resolve an import specifier to a real file. '@/' is the src alias, '.' is relative.""" if spec.startswith("@/"): base = "src/" + spec[2:] elif spec.startswith("."): base = os.path.normpath(os.path.join(os.path.dirname(frm), spec)).replace("\\", "/") else: return None # external packages are never attached for ext in CANDIDATES: if base + ext in files: return base + ext return base if base in files else Noneout = {f: set() for f in files} # what f importsinn = {f: set() for f in files} # what imports ffor f in files: src = open(f, encoding="utf-8", errors="ignore").read() for spec in re.findall(r'from\s+["\']([^"\']+)["\']', src): r = resolve(spec, f) if r and r != f: out[f].add(r) inn[r].add(f)target = sys.argv[1]kb = lambda fs: round(sum(os.path.getsize(x) for x in fs) / 1024, 1)callers, deps = sorted(inn[target]), sorted(out[target])print(f"callers {len(callers)} / {kb(callers)} KB")for f in callers: print(" <-", f)print(f"dependencies {len(deps)} / {kb(deps)} KB")for f in deps: print(" ->", f)
Run it on src/lib/content.ts and you get 19 callers and zero dependencies. Zero dependencies because that module only reads HTML through the Cloudflare ASSETS binding and imports nothing of my own. What needed attaching was upward, not downward.
Cutting external packages inside resolve is deliberate. There is no room to attach node_modules, and the model already knows the types and behaviour of public libraries reasonably well. I would rather keep the attachment budget for information that exists only in my repository.
One hop, or two
Having decided to attach callers, the next question was depth. Should the callers of the callers come along too?
The measurement on the same repository was clear enough.
Selection
Files
Total size
Share of src
One hop (callers and dependencies)
19
132.9 KB
29.5%
Two hops (their neighbours as well)
33
220.9 KB
49.1%
Everything under src
68
449.9 KB
100%
One extra hop takes the attachment to nearly half the codebase. At that point it is hard to claim you selected anything; you are effectively sending the whole thing.
The 14 files added at hop two were, in my case, almost all UI components. When the article-retrieval contract changes, the first hop is what breaks directly, and the second hop settles once the first is fixed. So I stop at one hop now, and add named files only when something feels missing.
Long context is the other option, and I used to think that if it fits, it goes in. But the wider the slice, the harder it becomes to check which part the answer actually rested on. Narrowing the range is not about saving money. It is what makes verification possible.
Order of attachment, and the first sentence
How you hand the files over turns out to matter too. This is the routine I follow.
Pick one file to change and run deps.py to list its callers.
Keep only the callers that contain lines the change could affect. Tests and type-only files stay out at this stage.
Attach the file being changed first, then the callers. Order should not affect the result, but it makes the thread easier to re-read later.
In the first sentence, say which file is the target and which are callers. Something as plain as "I am changing content.ts; the rest are callers and stay as they are" is enough.
Ask for "the places that break, with file names and the role of the lines." Ask for abstract improvements and abstract improvements are what arrive.
Skip step four and you get rewrite suggestions for the callers as well. They are not bad suggestions, but what I wanted was the blast radius, not a redraft. Declaring roles up front made that mix-up almost disappear.
Two things that did not work
The first was attaching generated files. My repo has a JSON file of article metadata that is, on the dependency graph, a genuine caller. Its contents are a machine-emitted array, and reading it gave the model nothing to reason with. Generated files are now excluded, replaced by a single line describing their shape.
The second was underrating configuration. middleware.ts has zero callers and only two dependencies. Small on the graph, yet the branching there decides how pages behave. A small number on a dependency graph and a small blast radius are not the same thing. I made a comparable misjudgement about API keys, which I wrote down in why a replaced Gemini API key still loses to the old one.
Uploading a set of files removed the pasting, and nothing else. What it added was the work of choosing — and that work is answered by the shape of the repository, not by the model.
Choose the attachments from where things break, not from where you are typing. That order is the one thing I keep even on a rushed day.
Pick the file you touch most often, run it through deps.py, and see how many of its callers sit in the same folder. That single number tends to explain every attachment that has ever missed.
Thanks for sitting through a piece that is mostly counting.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.