Skip to content

feat: one repos.yml entry can be a folder of repos - #32

Merged
tschm merged 1 commit into
mainfrom
feat/folder-entries
Sep 2, 2026
Merged

feat: one repos.yml entry can be a folder of repos#32
tschm merged 1 commit into
mainfrom
feat/folder-entries

Conversation

@tschm

@tschm tschm commented Sep 2, 2026

Copy link
Copy Markdown
Member

- folder: ~/repos/jebel-quant puts every checkout sitting directly inside that
directory on the board. Sixteen lines become one, and the one cannot drift out
of step with the disk the way sixteen can: clone a repo into the folder and it
joins at the next restart, delete it and it leaves.

repos:
  - folder: ~/repos/jebel-quant     # every checkout directly inside it
  - path: ~/repos/cvxgrp/cvxrisk    # still one repo at a time where you want that

Not the walk we removed

The old board was assembled by a whole-org GitHub sweep plus a directory walk
under one mounted root, and both decided membership on their own. A folder is a
directory you wrote down, read one level deep and no further. folder: ~
finding your entire disk is exactly the failure the one-level rule prevents —
and it is also why folder: ~/repos correctly finds nothing when the checkouts
live at ~/repos/<org>/<name>.

The refusals

They follow what a path entry already does, for the same reason: a board
quietly short a folder's worth of repos is worse than one that did not come up.

Folder is not there refuses to start
Folder holds no checkouts refuses to start
A child that is not a checkout passed over
A clone with no usable origin warned and skipped

The last one is the only softening. That is somebody's scratch clone, and a
folder entry is not the deliberate statement that naming a path is — a path
to that same clone is still fatal. A folder of nothing but such clones still
refuses, since the file said something and none of it survived.

An entry of its own wins

The dedupe is on the checkout path, not on namespace/name. A folder leaves
out any checkout another entry names by path, so one repo inside a listed
folder keeps a repo: override for its upstream:

repos:
  - folder: ~/repos/forks
  - path: ~/repos/forks/cvxpy     # a fork; the board follows upstream
    repo: cvxpy/cvxpy

Matching on the name could not do that, because the whole point of the override
is that the name comes out different. Where the collision really is on the name
(- repo: org/x beside the folder org/x is checked out in), the two merge
into one repo either way round: the entry decides the name and the forge, the
folder still supplies the path it found. Two folders holding clones of the same
repo remain a refusal — there is no saying which checkout the board means.

forge: gitlab on a folder covers every checkout it turns up, validated once
against the line that was written rather than per checkout found.

Verification

  • 438 tests pass; repos.py at 100% statement coverage; ruff check and format
    clean.
  • Run against the real fleet on this machine: two folders plus a path
    override plus a bare repo: resolve to 11 repos, with the override renamed
    and the bare entry picking up a checkout path it could not previously
    express.
  • Nothing changes for a fleet with no folder: in it, and the container needs
    no new mount — the single read-only $HOME mount already covers folders.

Docs, repos.example.yml, README and docs/index.md updated, including the
"nothing is discovered" claim, which now reads "nothing is discovered behind
your back: a repo is on the board because repos.yml names it — or names the
folder you keep it in".

🤖 Generated with Claude Code

`- folder: ~/repos/jebel-quant` puts every checkout sitting directly inside
that directory on the board. Sixteen lines become one, and the one cannot
drift out of step with the disk the way sixteen can: clone a repo into the
folder and it joins at the next restart, delete it and it leaves.

This is not the whole-root directory walk that the explicit list replaced.
That walk took any checkout anywhere under one mount whose origin happened to
look plausible, and nobody had said where to look. A folder is a directory you
wrote down, read one level deep and no further - `folder: ~` finding your
entire disk is exactly the failure the one-level rule exists to prevent, and it
is also why `folder: ~/repos` correctly finds nothing when the checkouts live
at `~/repos/<org>/<name>`.

The refusals follow what a `path` entry already does, for the same reason: a
board that is quietly short a folder's worth of repos is worse than one that
did not come up. A folder that is not there, or that holds no checkouts at
all, stops the collector. What is *not* fatal is a clone inside the folder
with no usable origin remote - somebody's scratch clone, warned about and
skipped, because a folder entry is not the deliberate statement that naming a
path is. A `path` to that same clone is still refused.

An entry of its own wins, and the dedupe that makes that work is on the
checkout path rather than on `namespace/name`. A folder leaves out any
checkout another entry names by `path`, so one repo inside a listed folder
keeps a `repo:` override for its upstream - and the whole point of such an
override is that the name comes out different, which is why matching on the
name cannot do it. Where the collision really is on the name (`- repo: org/x`
beside the folder org/x is checked out in), the two merge into one repo either
way round: the entry decides the name and the forge, the folder still supplies
the path it found.

Verified against the real fleet on this machine: two folders plus a `path`
override plus a bare `repo:` resolve to 11 repos, with the override renamed and
the bare entry picking up a checkout path it could not previously express.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tschm
tschm merged commit 66d8170 into main Sep 2, 2026
6 checks passed
@tschm
tschm deleted the feat/folder-entries branch September 2, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant