Submodules: nested projects mounted as secondary worktrees

A submodule splits a project into smaller reusable parts or vendors a dependency; the goal is a seamless recursive experience where clone, checkout, and commit descend into subs by default. The method reuses Beagle's own machinery: a submodule is just another project in the parent's Store, checked out as a secondary worktree at the gitlink path. Git stays byte-exact — a 160000 gitlink and a root .gitmodules blob name the pin and upstream. be recurses by forking a child into each mount.

Inner workings

A submodule is a secondary worktree of a separate project: the parent tree's gitlink pins which commit, .gitmodules names where to fetch it, and recursion is process-level — be forks a child into each mount.

CLI

Recursion is the default — every verb descends into mounted subs; --nosub opts out for one invocation. Clones flow top-down, commits bottom-up.

API

Four headers split by dog: dog/git/SUBS is the pure .gitmodules parser/synth, keeper/SUBS enumerates off a tree, sniff/SUBS drives mounts, beagle/SUBS orchestrates recursion.

Git compatibility

Beagle reads and writes submodules exactly as git does — 160000 gitlinks in the parent tree and a gitconfig-format .gitmodules blob — so a beagle-cloned repo round-trips through git untouched.

Store layout

A submodule is just another project shard in the parent's store, mounted as a secondary worktree that points back; there is no separate submodule store class.

TLV report aggregation (landed)

The parent captures each sub's TLV hunk report and relays every hunk into its sequential stream, rebasing only the path prefix — one be lists affected files across the whole tree, for every verb.

Committing detached subs (synthetic branch)

A mounted sub sits detached at the gitlink pin; a parent POST commits it onto a synthetic branch so the new commit is a real ref tip, never a GC-orphan.