# oldcircle/geo-sleuth architecture

> Architecture diagram of the GitHub repository oldcircle/geo-sleuth, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-28). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/oldcircle/geo-sleuth
- Repository: https://github.com/oldcircle/geo-sleuth

## Overview

geo-sleuth is an Agent Skill for geolocating a photo through evidence-led, script-assisted investigation. The main workflow starts with photo intake, develops visual and metadata clues, searches candidate regions and sites, then tests camera position and viewing geometry before producing coordinates, heading, confidence and an evidence image. The README documents additional search and verification capabilities not represented in the sampled source excerpts; those are included at subsystem level, with uncertain internal wiring left out.

Main entry points: [`skills/geo-sleuth/SKILL.md`](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/SKILL.md) (Agent workflow), [`skills/geo-sleuth/scripts/intake.py`](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/intake.py) (Intake report), [`skills/geo-sleuth/scripts/revimg.py`](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/revimg.py) (Image search), [`skills/geo-sleuth/scripts/clues.py`](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/clues.py) (Reference lookups)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_intake["Photo intake"]
  node_skill["Agent workflow<br/>[SKILL.md]"]
  node_intake_script["Intake report<br/>[intake.py]"]
  node_exif["EXIF reader<br/>[exif.py]"]
  node_imgprep["Image preparation<br/>[imgprep.py]"]
  node_ocr["Text recognition<br/>[ocr.py]"]
end

subgraph group_clues["Clue analysis"]
  node_revimg["Image search<br/>[revimg.py]"]
  node_clues_script["Reference lookups<br/>[clues.py]"]
  node_data[("Clue tables")]
  node_sun["Sun and shadows<br/>[sun.py]"]
  node_board[("Candidate evidence board<br/>[board.py]")]
end

subgraph group_search["Candidate search"]
  node_osm["OSM feature search<br/>[osm.py]"]
  node_gazetteer["Administrative regions<br/>[gazetteer.py]"]
  node_sat_scan["Satellite scan<br/>[sat_scan.py]"]
  node_tiles["Map imagery<br/>[tiles.py]"]
end

subgraph group_verify["Position verification"]
  node_terrain["Terrain and skyline<br/>[terrain.py]"]
  node_pose["Camera pose fit<br/>[pose.py]"]
  node_match["Scene ranking<br/>[match.py]"]
  node_geo["Geometric calculations<br/>[geo.py]"]
end

subgraph group_output["Evidence and results"]
  node_evidence["Evidence generation<br/>[evidence.py]"]
  node_result["Location and confidence"]
end

node_user(("Photo submitter"))
node_osm_service(("OpenStreetMap services"))
node_elevation[("Elevation data")]
node_imagery_service(("Satellite imagery services"))
node_streetview(("Street-view services"))
node_search_engines(("Image search engines"))

node_user -->|"provides photo"| node_skill
node_skill -->|"runs intake"| node_intake_script
node_intake_script -->|"reads metadata"| node_exif
node_intake_script -->|"prepares variants"| node_imgprep
node_intake_script -->|"extracts text"| node_ocr
node_intake_script -->|"searches images"| node_revimg
node_revimg -.->|"queries"| node_search_engines
node_clues_script -->|"reads tables"| node_data
node_skill -->|"checks clues"| node_clues_script
node_skill -->|"tests shadows"| node_sun
node_skill -->|"tracks candidates"| node_board
node_skill -->|"queries features"| node_osm
node_skill -->|"enumerates regions"| node_gazetteer
node_osm -->|"queries"| node_osm_service
node_gazetteer -->|"queries"| node_osm_service
node_skill -->|"ranks target areas"| node_sat_scan
node_sat_scan -->|"requests tiles"| node_tiles
node_tiles -.->|"fetches imagery"| node_imagery_service
node_skill -->|"fits skylines"| node_terrain
node_terrain -.->|"samples terrain"| node_elevation
node_skill -->|"fits camera pose"| node_pose
node_skill -->|"ranks scenes"| node_match
node_match -.->|"renders candidates"| node_streetview
node_skill -->|"computes geometry"| node_geo
node_skill -->|"builds evidence"| node_evidence
node_evidence -->|"produces report"| node_result
node_skill -->|"returns location"| node_result

click node_skill "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/SKILL.md"
click node_intake_script "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/intake.py"
click node_exif "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/exif.py"
click node_imgprep "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/imgprep.py"
click node_ocr "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/ocr.py"
click node_revimg "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/revimg.py"
click node_clues_script "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/clues.py"
click node_data "https://github.com/oldcircle/geo-sleuth/tree/main/skills/geo-sleuth/data"
click node_sun "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/sun.py"
click node_board "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/board.py"
click node_osm "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/osm.py"
click node_gazetteer "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/gazetteer.py"
click node_sat_scan "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/sat_scan.py"
click node_tiles "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/tiles.py"
click node_terrain "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/terrain.py"
click node_pose "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/pose.py"
click node_match "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/match.py"
click node_geo "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/geo.py"
click node_evidence "https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/evidence.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_skill,node_intake_script,node_exif,node_imgprep,node_ocr,node_streetview toneBlue
class node_revimg,node_clues_script,node_data,node_sun,node_board,node_elevation toneAmber
class node_osm,node_gazetteer,node_sat_scan,node_tiles toneMint
class node_terrain,node_pose,node_match,node_geo toneRose
class node_evidence,node_result,node_user,node_osm_service,node_imagery_service,node_search_engines toneIndigo
```

## Components

### Photo intake

- [Agent workflow](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/SKILL.md): `skills/geo-sleuth/SKILL.md`
- [Intake report](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/intake.py): `skills/geo-sleuth/scripts/intake.py`
- [EXIF reader](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/exif.py): `skills/geo-sleuth/scripts/exif.py`
- [Image preparation](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/imgprep.py): `skills/geo-sleuth/scripts/imgprep.py`
- [Text recognition](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/ocr.py): `skills/geo-sleuth/scripts/ocr.py`

### Clue analysis

- [Image search](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/revimg.py): `skills/geo-sleuth/scripts/revimg.py`
- [Reference lookups](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/clues.py): `skills/geo-sleuth/scripts/clues.py`
- [Clue tables](https://github.com/oldcircle/geo-sleuth/tree/main/skills/geo-sleuth/data): `skills/geo-sleuth/data`
- [Sun and shadows](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/sun.py): `skills/geo-sleuth/scripts/sun.py`
- [Candidate evidence board](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/board.py): `skills/geo-sleuth/scripts/board.py`

### Candidate search

- [OSM feature search](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/osm.py): `skills/geo-sleuth/scripts/osm.py`
- [Administrative regions](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/gazetteer.py): `skills/geo-sleuth/scripts/gazetteer.py`
- [Satellite scan](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/sat_scan.py): `skills/geo-sleuth/scripts/sat_scan.py`
- [Map imagery](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/tiles.py): `skills/geo-sleuth/scripts/tiles.py`

### Position verification

- [Terrain and skyline](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/terrain.py): `skills/geo-sleuth/scripts/terrain.py`
- [Camera pose fit](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/pose.py): `skills/geo-sleuth/scripts/pose.py`
- [Scene ranking](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/match.py): `skills/geo-sleuth/scripts/match.py`
- [Geometric calculations](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/geo.py): `skills/geo-sleuth/scripts/geo.py`

### Evidence and results

- [Evidence generation](https://github.com/oldcircle/geo-sleuth/blob/main/skills/geo-sleuth/scripts/evidence.py): `skills/geo-sleuth/scripts/evidence.py`
- **Location and confidence**

### Other components

- **Photo submitter**
- **OpenStreetMap services**
- **Elevation data**
- **Satellite imagery services**
- **Street-view services**
- **Image search engines**

## Connections

- Photo submitter → Agent workflow: provides photo
- Agent workflow → Intake report: runs intake
- Intake report → EXIF reader: reads metadata
- Intake report → Image preparation: prepares variants
- Intake report → Text recognition: extracts text
- Intake report → Image search: searches images
- Image search → Image search engines: queries
- Reference lookups → Clue tables: reads tables
- Agent workflow → Reference lookups: checks clues
- Agent workflow → Sun and shadows: tests shadows
- Agent workflow → Candidate evidence board: tracks candidates
- Agent workflow → OSM feature search: queries features
- Agent workflow → Administrative regions: enumerates regions
- OSM feature search → OpenStreetMap services: queries
- Administrative regions → OpenStreetMap services: queries
- Agent workflow → Satellite scan: ranks target areas
- Satellite scan → Map imagery: requests tiles
- Map imagery → Satellite imagery services: fetches imagery
- Agent workflow → Terrain and skyline: fits skylines
- Terrain and skyline → Elevation data: samples terrain
- Agent workflow → Camera pose fit: fits camera pose
- Agent workflow → Scene ranking: ranks scenes
- Scene ranking → Street-view services: renders candidates
- Agent workflow → Geometric calculations: computes geometry
- Agent workflow → Evidence generation: builds evidence
- Evidence generation → Location and confidence: produces report
- Agent workflow → Location and confidence: returns location
