Skip to content

The research tree ​

The research tree is one directory that holds all your research work: the packages, the experiments, the papers, the notes and the literature. Each part of the work is a git repository of its own, in a directory that says what kind of work it is. The harness knows this layout. It clones and syncs the repositories, it gives each of them the same checks and workflows, and each directory gets the rules and the agents for its kind of work.

This page describes the tree of the harness's author as an example, then how to adapt it, and then the commands that work on the whole tree. In the example, the tree is ~/Research.

Why one tree ​

  • A session can reach every repository. A change often touches more than one: a package and the experiment that uses it, or a paper and the script that checks its claims. The settings give a session the tree as one working directory, and the sandbox lets a shell command write in it.
  • Each repository keeps its own history. It has its own remote, its own tests and its own CHANGELOG.md. In the example, a change to a package or an experiment goes through a pull request, and a change to the notes goes to main directly.
  • The directory says what kind of work it is. A rule, an instruction file or a skill can apply to one directory only, so a session in the papers does not load the rules of the packages.
  • Temporary work has fixed places. A worktree and a log go to directories that the guard hooks know, not into a repository.

An example layout ​

The overview figure of the Introduction shows six directories of the example tree. Each row below names the components of the harness that work on one of them, and links each to its section in Components.

directorywhat it holdsthe components that serve it
Librarya catalogue of the papers and books of other authors: for each paper, a directory with its file and a short Markdown description; in the example also the citation keys and the tools that build the shared bibliographyliterature-scout, which says when a paper is already in the library; harness wiki-lint, which checks the paths and the line citations; pre-commit-wiki, which runs it before each commit
Knowledgea vault of Markdown notes that record what is established: facts, methods and measurements, with links between the notesharness wiki-lint with every check, and pre-commit-wiki; changelog-scribe, for a change to what is established; math-verify, whose scripts the notes link; wait-what
Packagesthe software packages, Julia packages in the example, each with its tests, its documentation and a CHANGELOG.mdthe agents and skills of a planned change and of a pull request, from Agentic software engineering; the Julia skills julia-structure, julia-package-audit, julia-surgical-fix and julia-release; the agents of a diagnosis, such as julia-load-doctor; the rules julia-code, julia-tests, julia-docs, changelog and known-issues; the shared git hooks and workflows of Git hooks and workflows
Experimentsthe numerical experiments: code with reproducible results, its tests and a CHANGELOG.mdthe same agents of a planned change and of a pull request as Packages; julia-perf-analyst and the skill julia-performance, for a measurement; the rules julia-code, julia-tests and changelog; the same git hooks
Projectsderivations and analysis: notes, calculations and scripts, with a research log of what was tried and what it showedmath-verify; changelog-scribe, for the research log; julia-builder, whose part ends in a commit to main here, not in a pull request
Papersthe manuscripts, as LaTeX sources, each with the scripts that check its claimslatex-revision; latex-verifier; math-verify; literature-scout; changelog-scribe, for a dated entry of each revision pass; editor and writer, for one unit written from a brief

worker reads the directory's own instruction file before its first edit in Packages, Experiments, Projects or Papers.

The tree has four more directories that the subcommands of the harness command know:

directorywhat it holds
Environmentthe notes about the machine and the harness, with the evidence behind each rule, and, in the example, the tree instructions
Tasksthe task files: one Markdown plan for each task, with its parts table and the state of each part
Books and Talksthe books and the talks, one repository each, as in Papers

Your own layout ​

Your tree can have other directories and other names. Two values of the profile describe it. examples/profile.toml has them below the home directory, ~:

toml
repository_roots = ["~/Research/Packages", "~/Research/Experiments"]
tree_agents = "~/Research/Environment/Agents"
  • repository_roots lists the directories that contain the repositories of the tree. harness leaks searches the harness for the name of each directory below them, so that no source of the harness names one of your repositories.
  • tree_agents holds the directory of your tree instructions. harness installinstalls them into ~/.claude/ beside the sources of the harness. The directory can be anywhere.

Some subcommands do not take their directories from the profile. They find them by a fixed name below the research tree, $RESEARCH_ROOT, or ~/Research if it is not set:

  • harness githooks, harness workflows, harness ci-protection and harness format look in Packages and Experiments. harness ci-protection also protects the Harness checkout that runs it.
  • harness clone and harness repos look in the group directories Packages, Experiments, Papers, Books, Talks and Projects, and for the fixed repositories Knowledge, Environment, Tasks and Library.
  • harness wiki-lint-hook looks for the prose repositories, Knowledge and Library among them. harness wiki-lint reads the same names.

A subcommand that finds no directory with its name skips it and changes nothing there. To use these subcommands, keep these names for the directories of your repositories.

The settings template and the guard hooks name ~/Research itself: the working directory of a session, the write allowlist of the sandbox, and the places of the worktrees and of the scratch directory. $RESEARCH_ROOT does not move them. So keep the tree at ~/Research, or change the template and the hooks.

Instructions for each directory ​

The tree instructions hold what is true of your tree and of no other: the names of your directories, the instruction file of each, and where a change goes. adapters/claude/CLAUDE.md imports their instructions/research-tree.md, and OpenCode, oh-my-pi and Antigravity read that file too. Architecture describes the layer.

In the example, each directory of the figure has its own instruction file, CLAUDE.md, and the tree instructions name it. Claude Code loads a CLAUDE.md when a session works in its directory, so the conventions of the papers load only in the papers. A rule loads when the agent reads a file that its paths: field matches, wherever the file is. Rules and instructions describes both.

Temporary work ​

Two directories at the top of the tree hold work that is not a repository of the tree. Their names start with a ., so they are not part of the layout above.

  • .worktrees/ holds the worktrees: a second checkout of a repository on another branch, for a pull request or a part of a plan. The hook worktree.py creates each one as ~/Research/.worktrees/<Repository>-<slug>, from origin/HEAD. Remove a worktree with git worktree remove from its main checkout when its branch is merged.
  • .scratch/ holds the logs, the probe environments and the results of the tests that run outside a repository. It holds no checkout.

The rm check of the guard hook lets a recursive rm run only below these two directories.

Clone, sync and trust ​

These subcommands act on every repository of the tree. Each subcommand that changes something is a dry run first, and --apply makes the change. The harness command gives the contract of each.

subcommandwhat it does
harness githookscopies the shared git hooks of githooks/ into .githooks/ of each git repository directly below Packages/ and Experiments/, and sets core.hooksPath there
harness workflowscopies the canonical files of githooks/workflows/ into each git repository directly below Packages/ and Experiments/ that has a Project.toml: the CI workflow, dependabot.yml and codecov.yml into each, and the TagBot workflow into each package. It copies the documentation workflow only where a tracked docs/make.jl exists, and never into a repository that the profile key docs_exceptions or docs_additions names. It deletes the retired CompatHelper.yml
harness trustmarks the research tree and each git repository below it as trusted in Claude Code's ~/.claude.json
harness cloneclones each repository of the repository table (see The harness command) that has no directory yet. A selection such as packages or knowledge limits it to one group or one fixed repository. A directory that is in the way stops the run before the first clone. After the clones, run harness githooks, harness wiki-lint-hook and harness trust with --apply
harness repos statusfetches every repository, then prints its branch, its commits ahead of and behind its upstream, its changed tracked files and its open pull requests, with gh for GitHub and glab for GitLab. It also names a repository of the table that is not cloned, a clone that the table does not list, and a repository with no origin. It is read-only, and takes no --apply
harness repos pullfetches, then fast-forwards each branch that is behind its upstream. It skips a branch that has diverged, and one whose incoming commits change a file with local changes
harness repos pushpushes each git repository below Packages/ and Experiments/ whose branch is ahead of its upstream, one at a time. It skips a repository with no upstream, one that is also behind its upstream, and one on a branch that is not the default branch, unless you give --include-topic-branches. --no-verify skips the pre-push suite
harness repos syncruns repos pull, then repos push
harness ci-protectionprotects the default branch on GitHub of each git repository directly below Packages/ and Experiments/ that has a CI.yml, with a ruleset that requires the status checks of the canonical CI.yml, and then of the Harness itself, with the jobs of its test.yml. It first checks that the workflows are canonical, and skips a private repository and one where you are not an admin
harness formatruns JuliaFormatter over each tracked .jl file of the repositories below Packages/ and Experiments/; it skips a repository that has no .JuliaFormatter.toml

Run each one in your own terminal, not from a session. The settings refuse a session's write to .git/config and to ~/.claude.json. A githooks run that cannot set core.hooksPath prints the command for each repository. repos push and repos sync run the pre-push hook of each repository, and a hook that runs a full test suite can take a long time.

status, pull and clone take every group (packages, experiments, papers, books, talks, projects) and every fixed repository (knowledge, environment, tasks, library) when you name none; push and sync take packages and experiments.

The copies of githooks and workflows are tracked files in each repository. Read the diff, then commit each copied file by name.

bash
harness githooks
harness githooks --apply
harness workflows --apply
harness trust --apply
harness repos status
harness repos pull --apply
harness repos push
harness format

Private data across machines says how the tree instructions, the profile and the memory reach a second machine.