Selected work / Code explorer
Threadmark
A map is useful when you can check where each edge came from.
Threadmark helps me follow dependencies through unfamiliar JavaScript, TypeScript and Python repositories. The Windows app and CLI share a Rust engine; source code stays on the machine.
- Project
- Personal software project
- Stack
- Rust, Tauri, React, SQLite, Tree-sitter
- Stage
- Early release candidate · v0.1.1
- Release
Windows builds are unsigned. Read installation notes and limits ↗
The question behind the interface
Searching a name gives me occurrences. It does not tell me whether one function calls another, which relationship was resolved, or what an edit might reach. Threadmark brings those questions into one view, then lets me open the source behind an answer.
The project includes the analysis engine, local persistence, CLI and desktop interface. The useful boundary is between understanding code and presenting it: the interface should ask for a neighbourhood, not become a second analysis engine.
Watch the desktop app · 45 seconds
Video transcript and setup
Start with forecast.ts in the relationship map. Select the import from forecast.test.ts to inspect its exact source evidence. Switch to Impact to see reverse static reachability, then expand the controller.ts evidence path. Static reachability does not guarantee runtime impact.
The code is a small TypeScript and Python fixture, not a production application.
Interface walkthrough
Open a step to inspect the capture. Each image can be opened at full size.
01 Follow a relationship
The repository tree, directed relationship map and inspector share the selected symbol.
02 Inspect the evidence
The source inspector places relationship evidence beside the code it came from.
03 Compare a change
The Changes view separates added, removed and modified symbols in a local Git comparison.
These Threadmark captures render the frontend with responses recorded from its Rust engine against local example repositories. They show the interface, not a native session recording. Capture details ↗
Try a small example
Which code depends on this?
This hand-written sample explains directed relationships. It does not run Threadmark or analyse your files.
function readConfig() { return { limit: 10 }; }
function loadItems() { return readConfig().limit; }
function renderList() { return loadItems(); }
function exportItems() { return loadItems(); }
readConfig
Direct dependencies: none in this sample.
Reverse reachability: loadItems, renderList, exportItems.
A path through a static graph is a reason to investigate. It does not prove that every caller will run.
Two boundaries worth keeping
One engine for two clients
Rust owns discovery, Tree-sitter adapters, resolution, graph queries and Git comparison. React owns interaction and layout. Both the desktop app and CLI call the same engine. This keeps a relationship's meaning consistent whichever way I inspect it.
The desktop requests up to 24 nodes in a neighbourhood. The engine marks limits explicitly, so a bounded view cannot silently imply that no other dependencies exist.
Identity is separate from location
Symbol IDs use repository, path, language, qualified name and kind. Line numbers are excluded. Adding blank lines should move the source span without making every symbol look newly added. Renaming a file still changes identity and currently appears as additions and removals.
Architecture and decisions ↗ · Why evidence belongs beside an edge →
A cache hit does not make the work free
Version 0.1.1, measured on 7 October 2026. Generated TypeScript fixtures, ten functions per file, single run.
| Files / functions | New index | Unchanged refresh | One-file update | Peak working set |
|---|---|---|---|---|
| 100 / 1,000 | 102 ms | 48 ms | 60 ms | 21 MiB |
| 1,000 / 10,000 | 673 ms | 307 ms | 383 ms | 86 MiB |
| 10,000 / 100,000 | 7.40 s | 3.52 s | 4.60 s | 731 MiB |
Conditions matter
Windows 11 x64 · Intel Core i7-1365U · 16 GB RAM · Rust 1.98.1 release build. No concurrent build. OS filesystem caches were not flushed: “new index” means a new database and parse cache, not a physically cold disk.
Whole-graph resolution and snapshot persistence still run during a refresh. Reusing parses helps, but does not remove those costs. The first adjacency load and broad real-world graphs add work not captured by a small warm query.
Method, reproduction command and raw results ↗ · Read the performance note →
Where the answers stop
Static evidence cannot resolve arbitrary runtime dispatch, reflection or computed imports. Reverse impact is graph reachability, not a prediction of execution or test coverage. Git renames are not yet tracked as continuous identities.
The next useful improvements are affected-subgraph resolution and more efficient persistence, followed by broader desktop and long-session measurements. Linux and macOS have not been validated.
Next: Benchlight →