# Meta-Analysis: False Positive Duplicate Tool Call Detection

**Date:** 2026-05-14  
**Scope:** `packages/orchestrator/src/agenticRunner.ts` — `proactivePrune()` + `_buildResourceKey()`

## Executive Summary

Two interrelated bugs were discovered in the proactive context pruning system:

1. **REG-62 semantic dedup was designed but never wired** — the `seenResource` Map and `_buildResourceKey()` method existed but were dead code
2. **`_buildResourceKey` for `file_read` ignored offset/limit** — all reads of the same file mapped to the same resource key regardless of which lines were being read

If REG-62 had been wired WITHOUT fixing #2, it would have caused **catastrophic false positives**: reading different sections of a large file would be treated as the same resource, causing the older section to be pruned, then re-requested, then flagged as a duplicate — an infinite loop of false positive detection.

## Root Cause Analysis

### Bug 1: Dead Code — REG-62 Never Wired

```
Line 2993: const seenResource = new Map<string, { turn: number; idx: number }>();
```

This Map was declared but never populated or checked. The `_buildResourceKey()` method (line ~4611) was defined but never called from `proactivePrune()`. The entire semantic dedup layer — designed to catch cross-tool reads of the same resource (e.g., `file_read("foo.ts")` + `shell("cat foo.ts")`) — was inert.

**Impact:** Without semantic dedup, the system relied solely on exact fingerprint matching (`_buildToolFingerprint`), which includes ALL arguments. This meant `file_read("foo.ts", offset=10)` and `file_read("foo.ts", offset=50)` were correctly treated as different calls by exact dedup. However, the semantic layer that should catch cross-tool duplicates was missing entirely.

### Bug 2: Resource Key Ignored Line Range

```typescript
// BEFORE (broken):
if (name === "file_read") {
  const p = String(a.path ?? a.file ?? "");
  return p ? `resource:file:${p}` : "";
}
```

All `file_read` calls to the same path produced `resource:file:foo.ts` — regardless of offset/limit. This is the exact false positive the user reported: reads of different lines in larger files would be seen as the same resource.

**Why this matters:** When semantic dedup IS active, two calls with the same resource key but different fingerprints trigger pruning of the older call. Without offset/limit in the key, reading line 10-50 and then reading line 200-250 of the same file would prune the first read — even though both sections contain unique, needed information.

## Fixes Applied

### REG-67: Include offset/limit in resource key

```typescript
// AFTER (fixed):
if (name === "file_read") {
  const p = String(a.path ?? a.file ?? "");
  if (!p) return "";
  const offset = a.offset != null ? Number(a.offset) : undefined;
  const limit = a.limit != null ? Number(a.limit) : undefined;
  if (offset !== undefined || limit !== undefined) {
    return `resource:file:${p}@${offset ?? 0}:${limit ?? "end"}`;
  }
  return `resource:file:${p}`;
}
```

Now `file_read("big.ts", offset=10, limit=50)` → `resource:file:big.ts@10:50`  
And `file_read("big.ts", offset=200, limit=50)` → `resource:file:big.ts@200:50`  
Different keys → no false positive dedup.

### REG-62 Wired: Semantic dedup now active

The `seenResource` Map is now populated and checked in `proactivePrune()`. When two different exact calls target the same resource (same `rkey`), the older one is pruned. This correctly catches:

- `file_read("foo.ts")` followed by `file_read("foo.ts")` — same resource, prune older
- `shell("cat foo.ts")` followed by `file_read("foo.ts")` — same resource, prune older
- `file_read("foo.ts", offset=10)` followed by `file_read("foo.ts", offset=10)` — same resource+range, prune older

And correctly does NOT prune:

- `file_read("foo.ts", offset=10)` followed by `file_read("foo.ts", offset=200)` — different ranges, keep both

## Memory System Self-Evaluation

### What the memory systems got right
- Previous sessions correctly identified the duplicate tool call problem and stored findings in `project/bug_fixes`
- The `inner_critic_findings` topic flagged the agenticRunner.ts as a high-risk file for regressions
- Task lessons from prior sessions guided toward reading before editing

### What the memory systems missed
- No memory entry flagged that REG-62 was dead code — the `seenResource` Map was declared but never used
- The `project/architecture` topic lists 88 tools but doesn't document the proactivePrune dedup layers
- No cross-reference between the "duplicate calls" bug pattern and the specific dedup mechanism that causes it

### Recommendations for memory improvement
1. **Dead code detection**: When storing architecture knowledge, flag declared-but-unused variables as potential bugs
2. **Layer documentation**: The dedup system has 3 layers (exact fingerprint, semantic resource, aged file) — this should be documented in `project/architecture`
3. **False positive tracking**: Add a `project/false_positive_patterns` topic for known scenarios where dedup incorrectly flags unique calls

## Verification

- TypeScript compilation: ✅ `tsc --noEmit` passes
- Grep verification: ✅ dedup/duplicate references found in 11 orchestrator source files
- Edit verification: ✅ REG-67 and REG-62 comments present in agenticRunner.ts
