Fixture trees for the [bootstrap].config_roots dotfiles composition. Run:
./run.sh # uses `mise` from PATH
MISE_BIN=/path/to/mise ./run.sh # any version; behavior per the table
Every case is a materialized tree under case*/. run.sh copies each into
a throwaway sandbox (mktemp -d) and runs mise with an isolated HOME —
nothing touches the real home. The script asserts the 2026.8.10 column
of the table: on current mise it exits 0. Cases 1 and 3 guard the
jdx/mise#12190 fix (they fail if the conflict returns), case 5 asserts the
open bug (it fails when mise starts rejecting that collision — the fix
detector), and 2, 4, 6 are controls pinning documented behavior.
| case | layout | 2026.8.9 | 2026.8.10 |
|---|---|---|---|
| 1 | two roots declare "~/.config/mise/" symlink-each, sources hold disjoint leaves |
bug: hard error conflicting dotfile declarations |
composes — fixed by jdx/mise#12190 |
| 2 | same bundles, distinct targets | composes | composes |
| 3 | case 1 tree, mise bootstrap invoked from bundles/a |
bug: same hard error | composes — the parent's config_roots is live in child invocations |
| 4 | two roots write managed blocks with distinct ids into one file | merges, keyed by (path, id) |
merges, keyed by (path, id) |
| 5 | root a: directory copy whose source contains collide.txt; root b: whole-file copy at <dir>/collide.txt |
bug: silent last-wins | bug (open): silent last-wins — no conflict; entries apply in glob order and b silently overwrites a's file; a's status reports differs forever; unapply --force from a removes both entries' files |
| 6 | two roots claim the same target with copy |
documented error conflicting dotfile declarations, both origins named |
same — documented on https://mise.jdx.dev/bootstrap.html |
Case 5 reproduces identically on 2026.8.9 and 2026.8.10: the collision
check has always been exact-path and never considered a directory entry's
source footprint. #12190 (landed in 2026.8.10) fixed the same-target
symlink-each conflict but did not close this gap — case 5 is not a
regression from that fix.
Two whole-path entries with identical targets are rejected before any
mutation (case 6), and same-target symlink-each composes by disjoint leaf
(case 1) — but nothing checks a directory entry's source footprint. When
one root's directory copy contains a file that another root declares
exactly, composition applies both and resolves the collision silently by
config order — against the documented "Independent roots never acquire
precedence from their order in config_roots".
Reject this footprint collision before any mutation, the way #12190
already does for same-target symlink-each leaves ("reject duplicate
leaves and file/directory footprint collisions before any mutation") — or,
if silent last-writer-wins is intended, document the nesting exception
explicitly, including on https://mise.jdx.dev/dotfiles.html, whose modes
table ("Directory copies are additive") currently reads as if such overlap
is safe.
This repo originally reproduced the 2026.8.9 behavior of cases 1 and 3
(same-target symlink-each, disjoint leaves → hard error); jdx/mise#12190
fixed that in 2026.8.10 and those cases now serve as regression guards.
Case 5, added later, was verified to reproduce on both versions. The
discovering dotfiles repo migrated to per-bundle scoped copy targets
(disjoint by construction); rendering one template next to a copied
directory exposed case 5's shape.