research-tree

module
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT

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.

Research Tree CLI screenshot


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_node selections, propagate depends_on degradation 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. provisionalvalidated | invalidatedsuperseded. 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 7 shows exactly what changed.
  • Chronological feed. rt feed --hours 24 --status done gives you a time- sorted activity view without losing the graph structure.
  • JSON output. Every command supports --json for 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, and aggregates keep 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 poisoned when evidence becomes unreliable, revalidate when trust is restored. rt doctor evidence traces contamination.
  • Structural diagnostics. rt lint audits hygiene; rt doctor lineage surfaces 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 automation
  • build/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 C is degraded
  • then both B and A are degraded in rt 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 migrate is transparent on legacy stores: historical done + outcome=unset nodes survive json↔bin migration (run rt storage repair-outcomes to 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 reindex rebuilds nodes.idx from nodes.bin; the store fails loudly instead of silently presenting an empty graph when the index is lost
  • research-graph binds to 127.0.0.1 by 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|umbrella distinguishes actionable work from program boundaries across CLI, ABI, server, Mermaid, and dashboard views
  • rt storage repair-outcomes provides the explicit migration path for legacy historical stores that still contain done + outcome=unset
  • restoring a legacy pre-repair snapshot is supported, but strict commands intentionally require rerunning rt storage repair-outcomes afterward

rt feature doctor computes health at read time:

  • poisoned implementation|fix|decision|regression node → degraded
  • poisoned benchmark|experiment node → warning
  • transitive depends_on chain to a degraded feature → degraded
  • direct collaborates_with link to a degraded feature → warning
  • missing created_from edge 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: cleansuspectpoisonedrevalidated
  • Contamination tracking: rt doctor evidence traces 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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL