README
¶
Research Tree
Research is a graph. Your logbook shouldn't be a flat file.
Research Tree maps engineering research as a directed acyclic graph (DAG). Every node is a unit of research — an idea, a claim, an experiment, a decision — connected by epistemic edges (parent, continuation, supersession).
It replaces linear research logs (logbooks, lab notebooks, markdown dumps) with a structured, queryable, and auditable knowledge graph.
Stack: Go standard library + cobra (CLI).
Storage: .research/ directory — no database, no server, no daemon.
License: MIT. See LICENSE.
Project status
Research Tree is a personal tool, released as-is under MIT. Fork or vendor
if you need a stable dependency. Bug reports and patches reviewed
opportunistically. See SUPPORT.md for the full maintenance policy.
0001 Hypothesis: sparse KD can recover recall
├─ 0002 Run: k=64 baseline [failure]
├─ 0003 Run: k=128 improved [success] ★ champion
│ └─ 0012 Poisoned: bad checkpoint ☣
└─ 0011 Revalidated: clean rerun ♻
0020 Inspired by 0001 — compares_against
Why this exists
Linear logs break down the moment research branches. You start with a hypothesis, run an experiment, pivot, fork, abandon a path, revisit it three weeks later. A flat text file can't answer:
- Which claims are still provisional? Which have been invalidated?
- What experiment produced this result, on which host, with what command?
- What did this node look like before I changed my mind?
- What depends on this assumption?
Research Tree gives you a DAG, not a timeline. Every node knows its parents, its children, its continuations, and its replacements. You navigate the structure of your thinking, not the order you typed it in.
It also preserves something ordinary tools usually lose: the pressure behind a conclusion. Git remembers code changes. Tests remember expectations. Issue trackers remember tasks. RT remembers a harder chain:
- what we tried to claim
- why we thought it was true
- what objection broke that claim
- how the claim had to narrow
- what evidence actually survived
That is why RT behaves more like scientific memory than a tracker. It keeps old objections alive long enough to discipline future work, instead of forcing each new agent to relearn the same caution from scratch.
It was born from tracking 19+ sequential log files during LLM prompt research. When backtracking to a three-week-old experiment took longer than running it again, the tool became necessary.

Five minutes to your first graph
# 1. Start a research project
rt init
# 2. Create a research node
rt node create --title "Hypothesis: larger context windows improve recall"
# 3. Claim hardware and log an experiment run
rt resource add --id ctx-gpu0 --label "gpu-node-3 gpu0" --kind gpu \
--endpoint gpu03.int.lab --endpoint-kind dns --tags cuda,24gb
rt resource claim 1 ctx-gpu0 --by codex --note "longqa baseline"
rt node logrun 1 \
--resource-id ctx-gpu0 \
--endpoint gpu03.int.lab \
--endpoint-kind dns \
--cmd "python eval.py --ctx 8192 --dataset longqa" \
--outdir /tmp/run-001 \
--seed 42 \
--note "baseline: 8k context, standard attention"
# 4. Validate the claim with evidence
rt node close 1 --outcome success \
--append-body "8k context improved recall by +12% over 4k baseline."
# 5. See what happened
rt feed --hours 24
rt resource history ctx-gpu0
rt tree
rt status
That's the core loop: hypothesis → experiment → evidence → conclusion. Every step is a node. Every connection is explicit. Nothing is lost.
The mental model
| Concept | What it means |
|---|---|
| Node | A unit of research: claim, experiment, decision, or observation |
| Node kind | work for actionable units, umbrella for program / roadmap / governance boundaries |
| Parent | What prior work does this depend on? (epistemic dependency) |
| Claim | A falsifiable statement. Starts provisional, ends validated, invalidated, or superseded |
| Run | A concrete experiment execution: resource, endpoint, command, outdir, seed, validity |
| Resource | A declared machine/GPU/cpu-slot with stable id, human label, and technical endpoint |
| Lease | A live occupancy claim from one node to one resource |
| Artifact | A file or dataset attached to a node (linked or embedded) |
| Supersede | A claim replaced by a better one — preserves both, with a traceable edge |
| Continue | A long investigation split across nodes — keeps the thread alive |
| Relation | A typed cross-edge: compares_against, inspired_by, depends_on, aggregates |
| Primary parent | Designated canonical parent when a node has multiple DAG parents |
| Poison | Flag a node's evidence as untrustworthy — propagates downstream via rt doctor |
| Revalidate | Restore trust in previously poisoned evidence |
A node with continued_by=[7] says "this work isn't done, node 7 picks it
up." A claim with superseded_by=[12] says "this conclusion was valid at the
time, but node 12 has a better one." A node with --relation compares_against:5
links to a baseline without polluting the parent lineage. No information is destroyed.
Another useful framing:
- Map: where a capability came from
- Compass: what frontier is legitimate next
- Constraint: what we still cannot honestly claim
- Adversarial memory: where we almost closed too early, and why that was wrong
The fourth function is what makes RT unusually sticky in long research loops. It does not just remember wins; it remembers where the project nearly lied to itself.
Key features
- Feature lineage + health. Features group many nodes into one living project entity,
preserve explicit
current_nodeselections, propagatedepends_ondegradation transitively, and expose doctor/timeline/impact/graph views. - DAG, not a tree. A node can have multiple parents from different branches. Ideas merge, and the graph reflects that.
- Claims with lifecycle.
provisional→validated|invalidated→superseded. Every transition is evidence-gated. - Experiment runs. Structured run records with resource, endpoint, command, outdir, seed, ETA, cost, and validity.
- Resource coordination. Explicit inventory plus active leases prevent agents from double-booking the same machine or GPU.
- Revision history and diffs. Every update creates a revision.
rt node diff 42 --rev-a 3 --rev-b 7shows exactly what changed. - Chronological feed.
rt feed --hours 24 --status donegives you a time- sorted activity view without losing the graph structure. - JSON output. Every command supports
--jsonfor scripting, dashboards, and agent integration. - Zero dependencies at rest. Everything is a directory on disk. No
database, no server, no daemon. Back up with
rsync. - Agent-ready. Both a Go ABI (
pkg/retree) and a structured CLI for external tooling. Designed to be embedded. - Institutional memory. Objections, poisoned evidence, superseded claims, and narrowed conclusions survive agent handoffs, context compaction, and long gaps in attention.
- Typed relations. Beyond parent-child:
compares_against,inspired_by,depends_on, andaggregateskeep the DAG clean without abusing--parents. - Feature lineage. Features group many nodes into one living project entity, with explicit cross-feature edges, evidence-based health, impact queries, and graph views.
- Evidence hygiene. Mark nodes as
poisonedwhen evidence becomes unreliable,revalidatewhen trust is restored.rt doctor evidencetraces contamination. - Structural diagnostics.
rt lintaudits hygiene;rt doctor lineagesurfaces multi-parent patterns, poisoned ancestors, and missing primary parents.
Install
go install github.com/frudas24/research-tree/cmd/rt@latest
Binary lands in $GOPATH/bin/rt. Add it to your PATH.
Or build from a clone:
go install ./cmd/rt
To build locally:
make build # -> build/rt
make libretree.so # -> build/libretree.so + build/libretree.h
Research Tree has two primary integration surfaces:
build/rt— standalone CLI for humans and shell automationbuild/libretree.so+build/libretree.h— C ABI for embedding into other software
Quick reference
rt init # create .research/ in current dir
rt status # dashboard: active, done, paused, claims, hotspots
rt status --matrix # status × outcome matrix
rt tree # visualize the DAG
rt tree --depth 2 # limit depth
rt tree --show-relations # show relation hints without changing DAG shape
rt tree --color always | grep '★' # inspect golden milestones in the graph
rt feed --by modified --hours 24 # recent activity
rt golden --verbose # list frontier/champion nodes
rt timeline --days 7 # activity grouped by day
rt changes --since 72 # nodes changed in last 72h
# Nodes
rt node create --title "..." --parents 1,2 --tags a,b,c
rt node create --kind umbrella --title "..." --parents 1,2
rt node create --title "..." --milestone-class golden --milestone-kind breakthrough --milestone-reason "..."
rt node create --title "..." --parents 1,2 --primary-parent 1 --relation compares_against:3
rt node show 42
rt node show 42 --agent --json # compact handoff for other agents
rt node edit 42 --title "better title" --add-tags urgent
rt node edit 42 --milestone-class golden --milestone-kind champion --milestone-reason "best lineage artifact"
rt node edit 42 --add-relation inspired_by:7
rt node edit 42 --append-body "new findings"
rt node close 42 --outcome success
rt node close 42 --outcome failure
rt node delete 42 --force
rt node diff 42 --rev-a 3
rt node ancestors 42
rt node descendants 42
rt node poison 42 --by agent --cause base_snapshot --reason "checkpoint MIA"
rt node revalidate 42 --by agent
# Experiments
rt resource claim 42 gpu-node-0 --by codex
rt node logrun 42 --resource-id gpu-node-0 --endpoint 10.0.0.14 --endpoint-kind ip --cmd "..." --outdir /tmp/run --seed 7
rt node link 42 --commit auto --artifact /tmp/run --host gpu-1
# Resources
rt resource add --id gpu-node-0 --label "GPU Node 0" --kind gpu --endpoint 10.0.0.14 --endpoint-kind ip
rt resource list --free
rt resource report
rt resource history gpu-node-0
# Queries
rt node list --status active
rt node list --kind umbrella
rt node list --tag benchmark
rt node list --claim-status invalidated
rt node list --milestone-class golden
rt node list --milestone-class golden --milestone-kind champion --sort-by modified --order desc
rt node list --body-contains "perplexity"
rt node list --created-after 2026-06-01T00:00:00Z --sort-by created
# Relations & hygiene
rt links # flat DAG edge view (parents + relations)
rt links --type compares_against # filter by relation type
rt lint # audit: parent fan-in, orphans, isolates
rt doctor lineage # structural parent issues, poisoned ancestors
rt doctor evidence # trace evidence contamination
# Feature lineage
rt feature create "RL Bridge" --from-node 7
rt feature link f0001 47 --role implementation
rt feature relate f0002 f0001 --type depends_on --from-node 58
rt feature doctor f0001
rt feature impact f0001
rt feature graph f0001
rt storage repair-outcomes
rt storage repair-outcomes --set 202=success --set 203=inconclusive
Umbrellas are first-class DAG nodes for research programs, roadmaps, or governance boundaries. They can have parents and children like any other node, but they are intentionally excluded from work-only hotspots so organizational structure does not inflate actionable pressure.
If you restore a historical snapshot that still contains legacy
status=done + outcome=unset nodes, strict commands such as rt status
will reject that store again until you rerun:
rt storage repair-outcomes
rt storage repair-outcomes --set 202=success --set 203=inconclusive
That is intentional: restore preserves history exactly, and the repair step is the explicit migration from legacy historical state back into the current strict model.
Feature lineage
Feature lineage adds a second layer above the research DAG:
- Nodes capture events and evidence.
- Features capture what is alive right now.
- Feature edges capture operational coupling between living entities.
current_node is derived from the latest linked implementation, fix, or
decision node unless it was set explicitly. Once CurrentNodeMode is
explicit, later feature links do not overwrite it.
depends_on health propagation is transitive:
- If
A depends_on B - and
B depends_on C - and
Cis degraded - then both
BandAare degraded inrt feature doctor
collaborates_with is intentionally weaker: it can warn on a directly degraded
collaborator, but it does not create infinite warning chains.
If feature_edges.jsonl is unreadable or corrupt, rt feature doctor fails
instead of returning a misleading partial-clean report.
Recent updates
As of v0.5.1, the main user-visible changes are:
- concurrent additive mutations preserve updates from every agent, while stale full-node replacements fail with an explicit revision conflict
- binary stores use recoverable BIN/IDX publication plus generation-checked lock-free reads; derived indexes reconcile from authoritative node state
- Windows readers and writers safely overlap BIN/IDX/generation replacement, with concurrent publication exercised under the race detector in CI
- embedded artifacts are crash-reconciled, inactive-node leases are repaired, and feature current nodes must belong to their feature
- CI and release publication require the complete test suite, race detector,
commentlint, and pinned
golangci-lint rt storage migrateis transparent on legacy stores: historicaldone + outcome=unsetnodes survive json↔bin migration (runrt storage repair-outcomesto close them), and legal higher-ID parent edges are preserved- audit-driven robustness fixes: strict CLI boolean flags, no silent codec
truncation, unique warning IDs, canonical run
endpoint_kind - node history stays readable across storage-format migration
rt storage reindexrebuildsnodes.idxfromnodes.bin; the store fails loudly instead of silently presenting an empty graph when the index is lostresearch-graphbinds to127.0.0.1by default and surfaces storage errors as 500 instead of rendering an empty graph- snapshot restore hardened against path-traversal archive entries
- JSON persistence is delta-based (no delete-all crash window); single-node reads are direct instead of full-store scans
- FFI bindings consolidated into
third_party/retree-bridge/covering the complete bridge ABI Node.kind=work|umbrelladistinguishes actionable work from program boundaries across CLI, ABI, server, Mermaid, and dashboard viewsrt storage repair-outcomesprovides the explicit migration path for legacy historical stores that still containdone + outcome=unset- restoring a legacy pre-repair snapshot is supported, but strict commands
intentionally require rerunning
rt storage repair-outcomesafterward
rt feature doctor computes health at read time:
- poisoned
implementation|fix|decision|regressionnode →degraded - poisoned
benchmark|experimentnode →warning - transitive
depends_onchain to a degraded feature →degraded - direct
collaborates_withlink to a degraded feature →warning - missing
created_fromedge anchor →unmoored
If feature_edges.jsonl cannot be read or is corrupt, rt feature doctor
fails loudly instead of returning a partial clean report.
Golden milestones
Golden milestones are a first-class metadata axis for frontier-significant nodes.
- Canonical storage/query fields:
milestone_class=golden- optional
milestone_kind=champion|breakthrough|pivot - required
milestone_reason
- Canonical query:
rt node list --milestone-class golden
- Human shortcut:
rt golden
Golden milestones are orthogonal to status, outcome, and claim_status.
A node is not golden merely because it is done, success, or validated.
Use golden metadata when the node breaks a ceiling, becomes the lineage champion,
or materially shifts the bottleneck/roadmap.
Evidence hygiene
When evidence becomes unreliable (corrupted checkpoint, bad dataset, broken toolchain), you can mark nodes as poisoned instead of deleting them:
# Mark a node's evidence as untrustworthy
rt node poison 42 --by agent --cause base_snapshot --reason "checkpoint mismatch"
# Trace contamination downstream
rt doctor evidence
rt doctor lineage --poisoned
# Restore trust when the issue is resolved
rt node revalidate 42 --by agent
- Evidence states:
clean→suspect→poisoned→revalidated - Contamination tracking:
rt doctor evidencetraces poison through the DAG - Never delete evidence. Poison preserves the record while flagging the risk. Deleting a node would orphan its children; poison keeps the graph intact.
Integrating with your agent or tool
Research Tree exposes two integration surfaces:
1. Go ABI (pkg/retree)
import "github.com/frudas24/research-tree/pkg/retree"
store, _ := retree.Open(".research")
node, _ := store.CreateNode(retree.Frontmatter{
Title: "Hypothesis: ...",
Parents: []retree.NodeID{1},
Tags: []string{"benchmark", "llm"},
})
store.CloseNode(42, retree.OutcomeSuccess, "conclusion text")
2. CLI with JSON
rt status --json
rt node show 42 --agent --json
rt node list --tag benchmark --json
rt feed --hours 24 --json
Parse the JSON from any language. No library needed.
3. Tell your agent to use Research Tree
Add this to your agent's project instructions (AGENT.md, .cursorrules, or system prompt):
## Research tracking
Use the `rt` CLI to maintain a research DAG in `.research/`. Every
non-trivial investigation gets a node:
- Hypothesis or claim → `rt node create --title "..."`
- Experiment run → `rt node logrun <id> --cmd "..." --seed 7`
- Evidence or conclusion → `rt node close <id> --outcome success`
- Cross-reference → `--relation compares_against:<other-id>`
Before starting new work, check `rt status` and `rt feed --hours 72`.
When an assumption is invalidated, mark it and check `rt lint` for
affected descendants.
Your agent will track branches, experiments, and evidence — without you having to remember what happened three weeks ago.
Design principles
- Epistemic edges, not organizational ones. A parent edge means "this depends on that claim or result," not "this belongs to that project."
- Evidence before conclusions. Claims must cite runs. Invalidated claims must cite a refuter node.
- The graph preserves history. Nothing is overwritten. Every revision is stored. Every superseded claim stays visible.
- Preserve pressure, not only the slogan. Keep the objection, failed assumption, or narrowed boundary that made a later claim defensible.
- Simplicity over features. Three statuses. Three outcomes. Four claim states. If a concept doesn't earn its place in the graph, it doesn't go in.
- Methodological accumulation. A past objection should become a present habit, not a forgotten anecdote.
- Portable by design.
.research/is a directory. Move it, back it up, version it. No migrations, no servers, no lock-in.
What this is not
Research Tree is not a task manager, project tracker, note app, or wiki. It does not replace Notion, Obsidian, Jira, Linear, or Logseq.
It is provenance infrastructure for engineering research: experiments, claims, failed branches, reruns, artifacts, evidence hygiene, and decisions where "what did we know at the time?" matters.
Documentation
| File | Content |
|---|---|
AGENT.md |
Project overview and conventions |
AGENT_INSTRUCTIONS.md |
Operational runbook for agents and contributors |
docs/cli.md |
Full CLI reference |
docs/AGENT_INSTRUCTIONS.md |
Extended agent guide |
docs/abi.md |
Go ABI reference |
docs/architecture.md |
Architecture overview |
docs/data-model.md |
Data model and types |
docs/storage.md |
.research/ persistence design |
docs/development.md |
Development guide |
docs/testing.md |
Testing guide |
docs/binary-codec.md |
Binary codec specification |
docs/e2e-simulator.md |
End-to-end simulator guide |
Release model
This repository is published as a usable infrastructure artifact, not as a managed product.
- License: MIT. Do what you want. Credit is appreciated, not required.
- Maintenance: best-effort only. See
SUPPORT.md. - ABI / CLI: intended to be integrable, but releases are still opinionated and pragmatic.
- Philosophy: build tools that respect how technical thinking actually works — branching, invalidation, reruns, and evidence-driven course correction.
Contributing
Small, well-scoped fixes are preferred over broad feature expansion. If you send a patch, include tests where practical and keep the DAG / provenance-first model intact.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
research-graph
command
Command research-graph serves a real-time DAG visualization for any research-tree root.
|
Command research-graph serves a real-time DAG visualization for any research-tree root. |
|
rt
command
Command rt is the CLI for research-tree, a tool for mapping scientific research as a directed acyclic graph (DAG) of nodes and edges.
|
Command rt is the CLI for research-tree, a tool for mapping scientific research as a directed acyclic graph (DAG) of nodes and edges. |
|
rt-bridge
command
Command rt-bridge produces a C shared library (libretree.so) that exposes the research-tree ABI for FFI consumption from TypeScript (bun:ffi) and other languages.
|
Command rt-bridge produces a C shared library (libretree.so) that exposes the research-tree ABI for FFI consumption from TypeScript (bun:ffi) and other languages. |
|
rt/cmds
Package cmds implements the CLI commands for the research-tree tool.
|
Package cmds implements the CLI commands for the research-tree tool. |
|
pkg
|
|
|
retree
Package retree provides the core data model and storage engine for research-tree, a tool for mapping scientific/research work as a directed acyclic graph (DAG).
|
Package retree provides the core data model and storage engine for research-tree, a tool for mapping scientific/research work as a directed acyclic graph (DAG). |
|
third_party
|
|
|
commentlint
command
|