Skip to main content
Glama
README.md
<p align="center">
  <img src="assets/readme/01-hero.svg" width="1000" alt="Ichnus" />
</p>

Ichnus is a stigmergic ant-swarm for bug localization. Isolated LLM ants score one file each against a bug report, deposit confidence-weighted heat onto a shared board, and spread it along the import graph with hop decay.

A median-of-3 pass catches random noise. A prosecutor / judge catches confidently wrong answers. You get a HOT / WARM / COLD markdown heatmap plus a full JSON audit trail — callable from Claude Code via MCP.

```bash
uv sync
ICHNOS_MOCK=0 uv run python -m ichnos2.demo_runner /path/to/repo \
  "query string drops the first parameter" ""
```

## Why it exists

Grepping a bug description finds *similar* code. Agents that read the whole repo burn tokens and still guess. Ichnus treats localization as a swarm search with a hard spend ceiling:

- **Ants never talk to each other** — they only write the board (O(A) cost, not O(A²))
- **Heat propagates along real import edges**, so neighbours of a hot file get scouted next
- **Total ant spend is bounded** — wave *i* uses `A0 / 2^i`, so spend stays `< 2 · A0`
- **Two verifiers, two failure modes** — median-of-3 for random noise; prosecutor/judge for confidently wrong answers

## Architecture

### System

<p align="center">
  <img src="assets/readme/04-system.svg" width="1000" alt="System architecture" />
</p>

### Pipeline

<p align="center">
  <img src="assets/readme/05-pipeline.svg" width="1000" alt="Pipeline" />
</p>

### Stigmergy

<p align="center">
  <img src="assets/readme/06-stigmergy.svg" width="1000" alt="Stigmergy loop" />
</p>

Ant-scored heat is a measurement and never evaporates. Propagated heat is a hypothesis and does. Recruitment makes this a swarm instead of a fixed ranked list: the next wave follows the trail.

### Verification

<p align="center">
  <img src="assets/readme/07-verify.svg" width="1000" alt="Verification" />
</p>

Stage 7 catches **random** variance. Stage 8 catches **systematic** error — a model that is confidently wrong every time. Median-of-3 cannot fix that; changing the question can.

## Quick start

Requires **Python 3.11+** and [`uv`](https://docs.astral.sh/uv/).

```bash
git clone https://github.com/Sharann-del/Ichnus.git
cd Ichnus
uv sync
```

### Offline demo (no API key)

```bash
uv run python -m ichnos2.demo_runner /path/to/repo \
  "login fails with empty password" ""
```

### Live run

Put a key in `.env` (never commit it):

```bash
GEMINI_API_KEY=...
# or
OPENROUTER_API_KEY=...
```

Then set mock mode explicitly whenever a key is present:

```bash
ICHNOS_MOCK=0 uv run python -m ichnos2.demo_runner /path/to/repo \
  "bug description" "optional stack trace"
```

| Variable | Purpose |
| --- | --- |
| `ICHNOS_MOCK` | `1` offline mock · `0` real LLM calls (required when a key is set) |
| `ICHNOS_PROVIDER` | `gemini` or `openrouter` (else inferred from which key exists) |
| `ICHNOS_ANT_MODEL` | Override the default cheap scout model |
| `ICHNOS_STIGMERGY` | `1` trail-following (default) · `0` static ranking A/B |
| `ICHNOS_DEBUG_ANTS` | `1` print client + raw text for the first few ants |
| `ICHNOS_PROSECUTOR_MODEL` | Optional separate model for Stage 8 |

Gemini free tier is the practical default for multi-ant waves. OpenRouter’s free quota is easy to exhaust on a mid-size repo; Ichnus fails fast with the reset time rather than spinning on a spent daily limit.

### Tests

```bash
uv run pytest tests/test_algorithm.py -v
```

**98 tests**, no network required.

### MCP (Claude Code / any MCP client)

`.mcp.json` in the project root:

```json
{
  "mcpServers": {
    "ichnus2": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "python3", "-m", "ichnos2.mcp_server"],
      "env": {
        "ICHNOS_MOCK": "0",
        "ICHNOS_PROVIDER": "gemini"
      }
    }
  }
}
```

Tool: `locate_bug_tool(repo_path, bug_description, stack_trace="")`  
Returns the same markdown heatmap as the CLI. Audit JSON lands at `<repo>/.ichnos2/last_audit.json`.

```bash
uv run python -m ichnos2.mcp_server
# or
uv run ichnos2-mcp
```

## What you get

**Markdown heatmap** — HOT / WARM / COLD bands with scores, reasons, line ranges, and verification notes.

**JSON audit** — full board state, ant verdicts, cache provenance, and demotion history under `.ichnos2/`.

The heatmap is a **suggestion for where to read next**, not a verdict. Keep using normal file tools after.

## How a wave spends budget

Wave *i* scouts roughly `A0 / 2^i` files. Across any finite number of waves:

```text
total spend = A0 · (2 − 2^(1−n))  <  2 · A0
```

This is asserted in the test suite (`test_total_budget_bound_proof`), not just documented.

Tests and example apps are excluded from scouting by default. A path named in a stack trace is always kept.

## Verified results

Measured on this codebase — not invented:

| Check | Result |
| --- | --- |
| Test suite | **98 passed** |
| Express real bug (prose symptom, no stack) | `lib/request.js` **HOT 10.0**, lines **230–241** (defect on 238) in **4/4** cold-cache Gemini runs, ~5–7s |
| Seeded Python bug via OpenRouter | Culprit HOT, defect line pinned; Stage 8 upheld |
| Mock e2e | Stage 8 overturns a planted confident decoy |
| MCP stdio | `locate_bug_tool` returns the heatmap end-to-end |
| Stage 1 recall (tiny synthetic sample) | **0.8 (4/5)** |

## Design choices that matter

- **Confidence-weighted deposit** — ranking uses `relevance × confidence`
- **Hop decay** — `γ^hop` from the nearest ant-sourced HOT file; cycle-safe BFS
- **Bounded demotion** — never below `floor × peak`
- **Provenance-aware cache** — mock and live scores never poison each other
- **Rate-limit aware client** — per-minute limits waited out; daily quota stops the run
- **Overlapping file windows** — strongest finding wins (averaging dilutes the truth)

## Limitations

- Accuracy is solidly shown on **one real bug** (Express) plus synthetics — not a broad hit-rate study
- Ants read **one file at a time**; they cannot run a repro or reason about whole-repo architecture
- Non-Python import edges are regex-based; only Python uses AST for authoritative contradictions
- “Embedding” similarity in Stage 1 is a **bag-of-words TF cosine proxy**, not a neural embedding
- Live mode sends file contents to Gemini or OpenRouter — no local-model path yet
- Free-tier quotas can make “fast” and “works” mutually exclusive

## Project layout

```text
ichnos2/
  locate_bug.py
  candidate_filter.py
  budget.py / priority.py
  ants.py
  board.py
  reliability.py
  verify.py
  aggregate.py
  llm_client.py
  mcp_server.py
  demo_runner.py
tests/
  test_algorithm.py
```

## License

[MIT](LICENSE) © 2026 Sharann Manojkumar

TDQS

A4.2/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of confusing it with another. The tool's purpose is clearly described and unambiguous.

Naming Consistency5/5

The single tool name 'locate_bug_tool' follows a clear verb_noun pattern and is internally consistent. There are no other names to contradict it.

Tool Count3/5

The server has exactly one tool, which feels thin for a typical server scope. However, the tool itself is substantial and not trivial, so it is borderline rather than severely undercounted.

Completeness5/5

The tool fully addresses its stated purpose of locating bug-related files and provides a comprehensive heatmap with evidence. There are no obvious missing operations within the narrow domain of bug localization.

Maintenance

ActivitySlowing
ResponsivenessNo issues