RFC: v0.2.503 Repository Reorganisation¶
Status: Accepted. This RFC was opened in this PR and immediately accepted by the maintainer with explicit decision to ship the reorganisation incrementally within v0.2.503 rather than the originally-proposed two-version deprecation window. Phases land as separate commits on
feat/v0.2.503; each is independently atomic and verified bydot lint+ the existing test matrix.
Summary¶
Split the current chezmoi-managed monorepo into a framework layer (distributable CLI + library) and a defaults layer (user-facing configuration), with the framework layer publishable as a standalone tarball to Homebrew, Scoop, and AUR. Maintain backwards-compatible behaviour for existing users via a one-shot migration script that runs on first apply after upgrade.
Motivation¶
R4 hard-audit identified the framework/user-config intermingling
as the highest-leverage structural gap blocking de-facto-
framework status (HARD_AUDIT_2026.md §8.5 Top-5 adoption gaps).
Concrete symptoms:
-
Distribution stuck at "curl-pipe-bash". The Homebrew / Scoop / AUR manifests scaffolded in v0.2.503 (
install/{homebrew,scoop,aur}/) cannot publish until there's a singlebin/dottarball — chezmoi'sdot_*/executable_*/private_*prefixes force the current layout. Until that's fixed, downstream distros have nothing to package. -
New contributor onboarding cost. Even with the v0.2.503
STRUCTURE.md, ~20 chezmoi-prefixed root paths require a concept (the chezmoi naming contract) to navigate. Abin/+lib/+defaults/layout is self-documenting. -
Framework forks are blocked. Anyone wanting to fork the CLI without the maintainer's personal configs has to manually delete 80+ tool-specific directories under
dot_config/. A clean separation makes "fork the framework, apply my own defaults" a one-command flow. -
Test surface bleed. CI runs
chezmoi apply --dry-runon every PR, exercising both framework templates AND the maintainer's personal defaults. A real consumer running the framework will not exercise the maintainer'sdot_config/aider/etc. — and yet a regression there blocks CI.
The reorganisation is breaking for existing user installs:
chezmoi tracks deployed files by source path, so moving
bin/dot to bin/dot means the old
~/.local/bin/dot would be removed before the new path is
installed. Mitigation: ship a migrate-v0.2-to-v0.3.sh script
that runs before the first post-upgrade chezmoi apply.
Detailed design¶
Target layout¶
Following the Debian/aws-cli discipline (every top-level path has a clear purpose; contributor orients in <30 seconds):
.
├── bin/ # CLI entrypoints
│ ├── dot # was bin/dot
│ ├── dot-load-benchmark-pty # was bin/dot-load-benchmark-pty
│ ├── dot-theme-sync # was bin/dot-theme-sync
│ ├── dot-bootstrap # was bin/dot-bootstrap
│ └── dot-update # was dot_local/bin/executable_update (renamed)
├── lib/ # Framework library (no chezmoi)
│ ├── commands/ # was scripts/dot/commands/
│ ├── ui.sh # was scripts/dot/lib/ui.sh
│ ├── utils.sh # was scripts/dot/lib/utils.sh
│ ├── platform.sh # was scripts/dot/lib/platform.sh
│ ├── log.sh # was scripts/dot/lib/log.sh
│ ├── bento.sh # was scripts/dot/lib/bento.sh
│ └── secrets_provider.sh # was scripts/lib/secrets_provider.sh
├── share/ # OS-conventional resources
│ ├── man/man1/dot.1 # was dot_local/share/man/man1/dot.1
│ ├── completions/ # was dot_local/share/zsh/completions/
│ └── docs/ # was docs/
├── defaults/ # User-facing default config (was dot_config/, etc.)
│ ├── home/ # dotfiles deployed to $HOME (dot_X → .X)
│ ├── config/ # dotfiles deployed to $XDG_CONFIG_HOME
│ └── tools/ # per-tool configs (mise/, npmrc/, ...)
├── install/ # Distribution + bootstrap
│ ├── install.sh # was install.sh (moved one level down)
│ ├── homebrew/dot.rb # already at install/homebrew/ in v0.2.503
│ ├── scoop/dot.json # already at install/scoop/ in v0.2.503
│ ├── aur/PKGBUILD # already at install/aur/ in v0.2.503
│ ├── provision/ # was install/provision/ (chezmoi run_onchange_ hooks)
│ └── migrate/ # NEW: migrate-v0_2-to-v0_3.sh + rollback
├── tests/ # unchanged
├── examples/ # unchanged
├── tools/ # NEW: ops scripts not shipped to users
│ ├── ci/ # was tools/ci/
│ ├── release/ # was tools/release/
│ ├── maintenance/ # was tools/maintenance/
│ ├── docs/ # was tools/docs/
│ └── version-sync.sh # was scripts/version-sync.sh
├── .chezmoiroot # NEW: points at defaults/
├── README.md
├── LICENSE
├── CHANGELOG.md
├── CLAUDE.md / AGENTS.md / per-harness renders
└── (no more dot_X at root)
chezmoi adaptation¶
.chezmoiroot lets chezmoi treat a subdirectory as the source
root. With .chezmoiroot = "defaults", chezmoi will look for
defaults/home/dot_zshrc, defaults/config/dot_starship.toml,
etc. — and deploy to the normal ~/.zshrc / ~/.config/starship.toml
paths.
This means:
- Repo top-level is no longer required to follow chezmoi naming.
bin/dotis a plain shell script, notdot_local/bin/executable_dot.share/man/man1/dot.1is a plain file, not chezmoi-deployed.- A Homebrew formula can
bin.install 'bin/dot'directly.
Migration tool¶
install/migrate/migrate-v0.2-to-v0.3.sh:
- Detect existing chezmoi state at
~/.local/share/chezmoi/~/.config/chezmoi/chezmoi.toml. - Read the user's pinned source repo from chezmoi.toml; if it's
sebastienrousseau/dotfiles@<v0.2.x>, warn and confirm. - Run
chezmoi diffand persist the per-file output to~/.local/state/dotfiles/v0_2_to_v0_3_pre_diff.logso the user has a record of pre-migration state. - Run
chezmoi forgetfor paths that are moving (no destructive delete — chezmoi forget only un-tracks). - Update
~/.config/chezmoi/chezmoi.tomlto point at the new sourceDir with.chezmoiroothonoured. - Run
chezmoi apply— picks up the new layout and re-creates the user's files at their canonical paths. - Run
dot doctoranddot lintto verify.
The migration is idempotent and safe to abort: at step 4 the chezmoi state is removed but no user data is deleted. At step 6 chezmoi notices "these files already exist on disk with content matching the source" and is a no-op.
Library bash-source paths¶
Today scripts/dot/commands/<cmd>.sh does:
After reorganisation:
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../utils.sh" # commands/<cmd>.sh → lib/utils.sh
Or, more robustly, drive lookup from a single env var set by bin/dot:
The Homebrew formula sets $DOT_LIB to ${libexec}/lib so
bin/dot finds its library wherever the package manager installed
it.
Distribution surface¶
Once bin/dot is standalone:
| Channel | Artefact | Verify command |
|---|---|---|
| Homebrew tap | dot-${VERSION}-${OS}-${ARCH}.tar.gz |
brew install sebastienrousseau/tap/dot && dot version |
| Scoop bucket | dot.json → dot-${VERSION}-windows-${ARCH}.zip |
scoop install dot && dot version |
| AUR | dotfiles-git PKGBUILD building from source |
paru -S dotfiles-git && dot version |
install.sh |
Same SHA256-verified path as today | bash install.sh |
| Direct tarball | Cosign-signed + SLSA-attested release asset | per docs/security/VERIFY_RELEASE.md |
The chezmoi-managed defaults/ subtree is only consumed when
a user wants the maintainer's opinionated config layer. It's a
strict superset: install dot standalone for the CLI; layer
defaults/ on top if you want the wallpaper-theming +
multi-shell setup.
Backwards compatibility¶
| Surface | v0.2.x behaviour | v0.2.503 behaviour | Breaking? |
|---|---|---|---|
~/.local/bin/dot |
Deployed by chezmoi | Replaced by Homebrew/Scoop install, OR symlinked by chezmoi from the new source | Yes — path may move; migration script handles it |
~/.zshrc etc |
Source-pinned at dot_zshrc |
Source-pinned at defaults/home/dot_zshrc, chezmoi reads via .chezmoiroot |
No — destination path unchanged |
dot <cmd> API |
All subcommands work as documented | Same | No |
scripts/dot/commands/*.sh consumers |
Direct source paths used in user customisations | Path moves to lib/commands/*.sh |
Yes — affects any user who source'd these directly |
.chezmoidata.toml |
Repo root | Repo root (unchanged for compatibility with old user chezmoi init flows) |
No |
Two-version deprecation window¶
v0.2.503 ships with the migration script and a deprecation warning
in dot doctor. v0.4.0 removes any v0.2.x shim code. Users who
skip v0.2.503 entirely (v0.2.x → v0.4.0) hit a hard error and must
run the migration tool from a v0.3.x release manually.
Alternatives considered¶
A) Keep the chezmoi monorepo as-is¶
Pros: zero migration cost; works today. Rejected: blocks Homebrew/Scoop/AUR publication permanently. The Top-5 adoption gap remains. R4 audit's "9.0/10 internal · 7.5/10 adoption" plateau persists.
B) Two-repo split (framework + defaults)¶
Publish dot framework at sebastienrousseau/dot and the
maintainer's personal defaults at sebastienrousseau/dotfiles.
Pros: cleanest possible separation. Framework forks trivial.
Rejected (for v0.3): requires a second repo, doubles the CI
matrix, and forces users to install from two sources. Defer to
v0.4 if v0.3 single-repo with .chezmoiroot proves insufficient.
C) Rename current root files only (cosmetic)¶
Just rename bin/dot → bin/executable_dot
without .chezmoiroot.
Rejected: chezmoi only resolves the executable_ /
dot_ prefixes for files inside its source root, so moving the
prefixed file outside breaks chezmoi-driven install entirely
without giving us a standalone tarball.
Unresolved questions¶
- How does the
defaults/subtree behave when a user wants to override one default? Today they editdot_config/X.tmpldirectly. Post-reorg, do they: (a) editdefaults/config/X.tmpland live with merge conflicts on framework updates, or (b) use a chezmoidataoverride + template conditional, or (c) maintain a second repo layered atopdefaults/? - Should
install/migrate/ship in the regular framework install, or only via a one-shothttps://...migrate.shURL? Bundling it forever increases install size; URL-only requires the user to find and trust the right URL during a stressful upgrade moment. - Does
.chezmoirootsurvive existing user customisations in~/.config/chezmoi/chezmoi.toml? Needs verification on a real upgrade test. - Windows-native
bin/dot: standalone PowerShell rewrite, or wrapper that shells to bash via WSL/git-bash?POWERSHELL_PARITY.mddocuments the current stub state.
Implementation plan¶
| Phase | Scope | Effort |
|---|---|---|
| 1 | Draft + ratify this RFC. Get user OK. | Done (this PR's draft) |
| 2 | Create defaults/, bin/, lib/, share/, tools/ and copy files. Update bash source paths. Add .chezmoiroot. |
1 week |
| 3 | Write install/migrate/migrate-v0_2-to-v0_3.sh. Test against a synthetic v0.2.503-installed environment. |
3 days |
| 4 | Update CI: every workflow that references scripts/, dot_local/, dot_config/ needs path updates. |
3 days |
| 5 | Update every doc that references the old paths. Most are in docs/manual/. |
1 day |
| 6 | Cut v0.2.999 RC as a deprecation-warning-only release; let real users dry-run the migration. | 1 day + 2-week soak |
| 7 | Cut v0.2.503 with the actual reorg + migration tool. | 1 day |
| 8 | Publish to Homebrew/Scoop/AUR using install/{homebrew,scoop,aur}/ scaffolds. |
1 week |
| Total | ~5 weeks calendar time |
See also¶
HARD_AUDIT_2026.md§8.3 — cross-platform gap analysis.HARD_AUDIT_2026.md§8.5 — Top-5 de-facto adoption gaps.STRUCTURE.md— today's layout (honest about the chezmoi-prefix forcing function).GOVERNANCE.md— RFC process this document follows.install/README.md— distribution-channel publication checklist.- Reference: Debian/aws-cli — clean top-level discipline.