google-gemini / google-gemini/gemini-cli

Flat memory import format ignores maxDepth (tree format honors it; CLAUDE.md-parity semantics violated)

Open Beginner friendly
#28,974 3 comments 0 reactions 0 assignees View on GitHub
area/agent status/need-triage
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

### Summary

The `flat` memory import format (`memory.importFormat: "flat"`) does not enforce the import depth limit that the `tree` format enforces. `processFlat` in `packages/core/src/utils/memoryImportProcessor.ts` accepts a `depth` parameter but never compares it against `importState.maxDepth`, so a deep `@import` chain is processed to unlimited depth in flat mode while the same chain truncates at depth 5 in tree mode.

This contradicts the project's own stated semantics: #2185 (which introduced this feature via #2230) explicitly targets CLAUDE.md parity, and the Claude Code memory docs it links specify imports "recurse to a depth of 5". The tree branch of the same function implements exactly that check; the flat branch added later omitted it.

### Repro

Create a 12-file chain where each file imports the next:

```bash
dir=$(mktemp -d); cd "$dir"
for i in $(seq 0 11); do
next=""; [ $i -lt 11 ] && next=" @f$((i+1)).md"
printf "CONTENT-F%s%s\n" "$i" "$next" > "f$i.md"
done
```

Run `processImports(root, dir, false, undefined, undefined, fmt)` for both formats:

| format | unique imported files present in output |
|---|---|
| `tree` | 6 (`f0`..`f5`, honoring `maxDepth=5`) |
| `flat` (main, unpatched) | **12** — full chain, no depth cap |

Test (fails on main, passes with fix below):

```ts
it('flat format honors maxDepth=5 exactly like tree format', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'depthchain-'));
const N = 12;
for (let i = 0; i < N; i++) {
const next = i + 1 < N ? ` @f${i + 1}.md` : '';
await fs.writeFile(path.join(dir, `f${i}.md`), `CONTENT-F${i}${next}\n`);
}
const root = await fs.readFile(path.join(dir, 'f0.md'), 'utf-8');
const tree = await processImports(root, dir, false, undefined, undefined, 'tree');
const flat = await processImports(root, dir, false, undefined, undefined, 'flat');
expect(new Set(tree.content.match(/CONTENT-F\d+/g)).size).toBe(6);
expect(new Set(flat.content.match(/CONTENT-F\d+/g)).size).toBe(6);
});
```

Verified against current main (`5411f113`): fails before the patch, passes after. Existing `memoryImportProcessor.test.ts` + `memoryDiscovery.test.ts` suites (56 tests) still pass with the fix.

### Root cause

In the `flat` branch of `processImports`, `processFlat(fileContent, fileBasePath, filePath, depth)` receives `depth` but never checks it. The only cycle protection is the `processedFiles` path set, which prevents infinite loops on repeated path strings but places no bound on chain depth. The `tree` branch guards at function entry with `if (importState.currentDepth >= importState.maxDepth)`, matching the intended CLAUDE.md-parity behavior from #2185.

Practical impact: a user (or a repo's checked-in GEMINI.md) relying on the documented depth-5 semantics gets arbitrarily deep context expansion in flat mode — unbounded token consumption in the model context from an import graph that should have been truncated.

### Fix

One guard in `processFlat`, mirroring the tree branch (using `>` so the file at depth == maxDepth is included with unresolved imports, exactly like tree mode):

```diff
--- a/packages/core/src/utils/memoryImportProcessor.ts
+++ b/packages/core/src/utils/memoryImportProcessor.ts
@@ -227,6 +227,16 @@
async function processFlat(
fileContent: string,
fileBasePath: string,
filePath: string,
depth: number,
) {
+ // Honor the same max depth limit as the tree format
+ if (depth > importState.maxDepth) {
+ if (debugMode) {
+ logger.warn(
+ `Maximum import depth (${importState.maxDepth}) reached at ${filePath}. Stopping flat import processing.`,
+ );
+ }
+ return;
+ }
+
// Normalize the file path to ensure consistent comparison
const normalizedPath = path.normalize(filePath);
```

Verification with the fix applied:
- Depth-chain repro: flat now yields 6 unique files, matching tree.
- Symlink self-import control still terminates correctly (unchanged).
- Full `memoryImportProcessor.test.ts` + `memoryDiscovery.test.ts`: 56/56 pass.

Happy to open a PR with the guard plus the regression test if maintainers want it.

---

*Side note from the filer: I work with AI coding agents daily and run a small service (FreshContext Pack) that fixes stale-doc/deprecated-API debt in AGENTS.md-style context files — https://deploy-foorge-team.vercel.app/sample-fresh-context-pack.html. No action needed on that; the issue above stands on its own.*

Contributor guide

Open the contributing guide

Research direction

Start in packages/core/src/utils/memoryImportProcessor.ts, comparing processFlat with the tree branch and its max-depth handling. Add or update the regression test in memoryImportProcessor.test.ts using the 12-file import chain, then run the memoryImportProcessor.test.ts and memoryDiscovery.test.ts suites. Done means flat and tree each include six files at maxDepth 5 and all 56 tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
90/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.