@sebastienrousseau/dotfiles 0.2.500 → 0.2.502

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +113 -45
  3. package/docs/.vitepress/reports/localization-readability-audit.md +4 -0
  4. package/docs/AI.md +8 -2
  5. package/docs/CNAME +1 -0
  6. package/docs/COPYRIGHT +1 -1
  7. package/docs/NAMING_CONVENTIONS.md +7 -0
  8. package/docs/README.md +4 -0
  9. package/docs/_config.yml +59 -0
  10. package/docs/adr/ADR-001-ci-cd-pipeline.md +17 -0
  11. package/docs/adr/ADR-002-shell-performance.md +9 -0
  12. package/docs/adr/ADR-003-security-first.md +18 -0
  13. package/docs/adr/ADR-004-cli-architecture.md +14 -0
  14. package/docs/adr/ADR-005-chezmoi-choice.md +10 -0
  15. package/docs/adr/ADR-006-shell-selection.md +9 -0
  16. package/docs/adr/ADR-007-multi-shell-parity.md +10 -1
  17. package/docs/adr/ADR-008-alias-system-architecture.md +10 -0
  18. package/docs/adr/ADR-009-wallpaper-driven-theming.md +131 -0
  19. package/docs/adr/ADR-010-starship-transient-prompt.md +144 -0
  20. package/docs/adr/ADR-011-nushell-tier3-keep.md +144 -0
  21. package/docs/adr/README.md +7 -0
  22. package/docs/architecture/ARCHITECTURE.md +4 -0
  23. package/docs/architecture/INTEROP.md +8 -0
  24. package/docs/architecture/REPO_LAYOUT.md +5 -1
  25. package/docs/architecture/WALKTHROUGH.md +4 -0
  26. package/docs/architecture/fleet-deployment.md +4 -0
  27. package/docs/archive/EUXIS_2026_REVIEW.md +11 -3
  28. package/docs/archive/LEGACY_ROADMAP.md +45 -27
  29. package/docs/archive/MILESTONE_v0.2.493.md +4 -0
  30. package/docs/archive/PLAN.md +46 -8
  31. package/docs/archive/REPO_AUDIT.md +8 -0
  32. package/docs/guides/INSTALL.md +4 -0
  33. package/docs/guides/NEOVIM_IDE_GUIDE.md +9 -0
  34. package/docs/guides/THEMING.md +8 -0
  35. package/docs/guides/TROUBLESHOOTING.md +28 -0
  36. package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +70 -1
  37. package/docs/index.md +6 -2
  38. package/docs/interop/A2A.md +7 -0
  39. package/docs/interop/POWERSHELL.md +102 -0
  40. package/docs/manual/00-introduction.md +5 -1
  41. package/docs/manual/01-concepts/01-architecture.md +4 -0
  42. package/docs/manual/01-concepts/02-trust-model.md +6 -2
  43. package/docs/manual/01-concepts/03-theme-engine.md +4 -0
  44. package/docs/manual/01-concepts/04-fleet.md +7 -1
  45. package/docs/manual/01-concepts/05-self-healing.md +8 -1
  46. package/docs/manual/02-tutorials/01-first-install.md +5 -0
  47. package/docs/manual/02-tutorials/02-add-wallpaper.md +5 -0
  48. package/docs/manual/02-tutorials/03-create-profile.md +6 -0
  49. package/docs/manual/02-tutorials/04-encrypt-secret.md +6 -0
  50. package/docs/manual/02-tutorials/05-deploy-fleet.md +5 -1
  51. package/docs/manual/03-reference/01-dot-cli.md +67 -45
  52. package/docs/manual/03-reference/02-config-files.md +8 -2
  53. package/docs/manual/03-reference/03-environment.md +4 -0
  54. package/docs/manual/03-reference/04-templates.md +7 -1
  55. package/docs/manual/03-reference/05-feature-flags.md +72 -3
  56. package/docs/manual/04-cookbook/01-recipes.md +4 -0
  57. package/docs/manual/04-cookbook/02-troubleshooting.md +32 -0
  58. package/docs/manual/04-cookbook/03-faq.md +8 -0
  59. package/docs/manual/05-appendices/A-platform-matrix.md +4 -0
  60. package/docs/manual/05-appendices/B-security-checklist.md +6 -0
  61. package/docs/manual/05-appendices/C-glossary.md +4 -0
  62. package/docs/manual/05-appendices/D-bibliography.md +5 -1
  63. package/docs/manual/05-appendices/E-license.md +4 -0
  64. package/docs/manual/_toc.yml +1 -1
  65. package/docs/manual/command-index.md +16 -8
  66. package/docs/manual/concept-index.md +4 -0
  67. package/docs/manual/index.md +66 -0
  68. package/docs/operations/ATTESTATION.md +5 -0
  69. package/docs/operations/CI_CADENCE.md +107 -0
  70. package/docs/operations/CI_COMPOSITES.md +156 -0
  71. package/docs/operations/COMPLETIONS.md +123 -0
  72. package/docs/operations/COVERAGE.md +182 -0
  73. package/docs/operations/DRIFT.md +107 -0
  74. package/docs/operations/HARD_AUDIT_2026.md +631 -0
  75. package/docs/operations/MAINTENANCE.md +5 -1
  76. package/docs/operations/MIGRATION.md +14 -6
  77. package/docs/operations/OPERATIONS.md +24 -0
  78. package/docs/operations/PERFORMANCE.md +133 -0
  79. package/docs/operations/REGISTRY.md +89 -0
  80. package/docs/operations/RELIABILITY.md +6 -0
  81. package/docs/operations/ROADMAP.md +36 -18
  82. package/docs/operations/ROADMAP_2026.md +665 -0
  83. package/docs/operations/TESTING.md +4 -0
  84. package/docs/operations/TRACEABILITY.md +7 -0
  85. package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +4 -0
  86. package/docs/operations/VERSION_SYNC.md +54 -5
  87. package/docs/reference/ALIASES.md +7 -0
  88. package/docs/reference/ALIASES_CHEATSHEET.md +5 -1
  89. package/docs/reference/ALIASES_DEPRECATIONS.md +5 -1
  90. package/docs/reference/FEATURES.md +4 -0
  91. package/docs/reference/FONTS.md +4 -0
  92. package/docs/reference/POWERSHELL_PARITY.md +80 -0
  93. package/docs/reference/PROFILES.md +4 -0
  94. package/docs/reference/SCREENSHOTS.md +4 -0
  95. package/docs/reference/SCRIPTS.md +4 -0
  96. package/docs/reference/SUPPORT_MATRIX.md +10 -4
  97. package/docs/reference/THEMES.md +4 -0
  98. package/docs/reference/TOOLS.md +4 -0
  99. package/docs/reference/UTILS.md +16 -12
  100. package/docs/registry.json +6 -0
  101. package/docs/security/AI_ACT_COMPLIANCE.md +4 -0
  102. package/docs/security/AUDIT_BYPASS.md +103 -0
  103. package/docs/security/AUTOMATION_SECRETS.md +4 -0
  104. package/docs/security/CI_EGRESS_ALLOWLIST.md +127 -0
  105. package/docs/security/CI_PINNING.md +113 -0
  106. package/docs/security/COMMIT_SIGNING.md +138 -0
  107. package/docs/security/COMPLIANCE.md +5 -0
  108. package/docs/security/DEPS_DEV_EXCEPTIONS.md +86 -0
  109. package/docs/security/DISCLOSURE.md +130 -0
  110. package/docs/security/ENCRYPTION.md +5 -0
  111. package/docs/security/FMEA.md +4 -0
  112. package/docs/security/HISTORY_FILTERING.md +132 -0
  113. package/docs/security/INCIDENT_RESPONSE.md +4 -0
  114. package/docs/security/INSTALL_VERIFICATION.md +122 -0
  115. package/docs/security/KEYS.md +4 -0
  116. package/docs/security/KEY_ROTATION.md +85 -1
  117. package/docs/security/MCP_POLICY.md +9 -0
  118. package/docs/security/POLICY_RELEASES.md +4 -0
  119. package/docs/security/README.md +4 -0
  120. package/docs/security/SCORECARD.md +140 -0
  121. package/docs/security/SECRETS.md +12 -0
  122. package/docs/security/SECURITY.md +4 -0
  123. package/docs/security/SECURITY_CHECKLIST.md +11 -0
  124. package/docs/security/SHELL_EXEMPTIONS.md +145 -0
  125. package/docs/security/SOUP_REGISTER.md +4 -0
  126. package/docs/security/THREAT_MODEL.md +10 -0
  127. package/docs/security/VERIFICATION_VALIDATION.md +5 -1
  128. package/docs/security/security-pubkey.asc +15 -0
  129. package/docs/themes/README.md +4 -0
  130. package/docs/themes/VISUAL_INTEGRITY_REPORT.md +4 -0
  131. package/dot_config/ai/identity.md +3 -0
  132. package/dot_config/ai/patterns/architect.md +2 -0
  133. package/dot_config/ai/patterns/hardener.md +2 -0
  134. package/dot_config/ai/patterns/refactor.md +2 -0
  135. package/dot_config/alacritty/alacritty.toml.tmpl +3 -3
  136. package/dot_config/atuin/config.toml.tmpl +47 -0
  137. package/dot_config/dotfiles/agent-card.json +1 -1
  138. package/dot_config/dotfiles/boot/README.md +2 -0
  139. package/dot_config/dotfiles/grub/README.md +2 -0
  140. package/dot_config/dotfiles/lock/README.md +2 -0
  141. package/dot_config/fish/conf.d/direnv.fish +4 -0
  142. package/dot_config/fish/conf.d/init.fish.tmpl +21 -0
  143. package/dot_config/fish/conf.d/mise-activate.fish +5 -0
  144. package/dot_config/fish/functions/_cached_eval.fish +84 -11
  145. package/dot_config/fish/functions/_cached_eval_clear.fish +17 -0
  146. package/dot_config/foot/foot.ini.tmpl +3 -3
  147. package/dot_config/fuzzel/fuzzel.ini.tmpl +2 -2
  148. package/dot_config/ghostty/config.tmpl +3 -3
  149. package/dot_config/git/hooks/executable_commit-msg +146 -0
  150. package/dot_config/goose/config.yaml +2 -2
  151. package/dot_config/gtk-3.0/gtk.css.tmpl +2 -2
  152. package/dot_config/gtk-3.0/settings.ini.tmpl +2 -2
  153. package/dot_config/gtk-4.0/gtk.css.tmpl +2 -2
  154. package/dot_config/gtk-4.0/settings.ini.tmpl +2 -2
  155. package/dot_config/kitty/kitty.conf.tmpl +3 -3
  156. package/dot_config/mise/config.toml +1 -1
  157. package/dot_config/niri/config.kdl.tmpl +2 -2
  158. package/dot_config/nushell/cached_eval.nu +80 -0
  159. package/dot_config/nushell/env.nu.tmpl +21 -13
  160. package/dot_config/shell/00-core-paths.sh.tmpl +9 -1
  161. package/dot_config/shell/05-core-safety.sh +1 -0
  162. package/dot_config/shell/10-secrets.sh +1 -0
  163. package/dot_config/shell/40-fzf-defaults.sh.tmpl +1 -0
  164. package/dot_config/shell/40-ls-colors.sh +1 -0
  165. package/dot_config/shell/50-logic-functions-core.sh.tmpl +1 -0
  166. package/dot_config/shell/50-logic-functions.sh.tmpl +1 -0
  167. package/dot_config/shell/51-logic-functions-extra.sh.tmpl +1 -0
  168. package/dot_config/shell/90-ux-aliases.sh.tmpl +1 -0
  169. package/dot_config/shell/91-ux-aliases-lazy.sh.tmpl +1 -0
  170. package/dot_config/shell/README.md +25 -8
  171. package/dot_config/starship.toml.tmpl +2 -2
  172. package/dot_config/tmux/tmux.conf.tmpl +3 -3
  173. package/dot_config/user-dirs.dirs +1 -0
  174. package/dot_config/vscode/settings.json.tmpl +2 -2
  175. package/dot_config/waybar/config.jsonc.tmpl +2 -2
  176. package/dot_config/waybar/style.css.tmpl +2 -2
  177. package/dot_config/wezterm/wezterm.lua.tmpl +3 -3
  178. package/dot_config/zsh/dot_zshrc.tmpl +207 -17
  179. package/dot_config/zsh/rc.d/00-alias-shims.zsh +28 -6
  180. package/dot_config/zsh/rc.d/30-options.zsh.tmpl +27 -7
  181. package/dot_local/bin/executable_bm +2 -0
  182. package/dot_local/bin/executable_dot +56 -9
  183. package/dot_local/bin/executable_dot-bootstrap +0 -1
  184. package/dot_local/bin/executable_dot-load-benchmark +1 -1
  185. package/dot_local/bin/executable_dot-theme-sync +8 -8
  186. package/dot_local/bin/executable_notify +2 -0
  187. package/dot_local/bin/executable_open +2 -0
  188. package/dot_local/bin/executable_tour +4 -2
  189. package/dot_local/bin/executable_up +3 -1
  190. package/dot_local/share/man/man1/dot.1 +1 -1
  191. package/dot_local/share/zsh/completions/_dot +4 -0
  192. package/install.sh +53 -38
  193. package/package.json +1 -1
  194. package/scripts/ci/check-dangerous-chmod.sh +19 -0
  195. package/scripts/ci/check-deps-dev.sh +236 -0
  196. package/scripts/ci/check-insecure-tls.sh +61 -0
  197. package/scripts/ci/check-regression-traceability.sh +67 -0
  198. package/scripts/ci/check-shell-preamble.sh +106 -0
  199. package/scripts/ci/dot-cli-startup-bench.sh +126 -0
  200. package/scripts/ci/install-chezmoi-verified.sh +4 -1
  201. package/scripts/ci/lint-reusable-pins.sh +78 -0
  202. package/scripts/ci/run-coverage.sh +451 -0
  203. package/scripts/ci/validate-chezmoidata.sh +25 -0
  204. package/scripts/ci/windows-smoke-test.ps1 +136 -0
  205. package/scripts/diagnostics/doctor.sh +203 -5
  206. package/scripts/diagnostics/drift-dashboard.sh +177 -13
  207. package/scripts/diagnostics/health.sh +21 -4
  208. package/scripts/diagnostics/perf.sh +304 -77
  209. package/scripts/diagnostics/workstation-attestation.sh +6 -1
  210. package/scripts/dot/commands/agent.sh +33 -27
  211. package/scripts/dot/commands/agents.sh +325 -0
  212. package/scripts/dot/commands/ai.sh +75 -10
  213. package/scripts/dot/commands/aliases.sh +10 -8
  214. package/scripts/dot/commands/core.sh +10 -4
  215. package/scripts/dot/commands/fleet.sh +278 -3
  216. package/scripts/dot/commands/init.sh +184 -0
  217. package/scripts/dot/commands/meta.sh +7 -4
  218. package/scripts/dot/commands/registry.sh +263 -0
  219. package/scripts/dot/commands/tools.sh +49 -1
  220. package/scripts/dot/lib/bento.sh +2 -1
  221. package/scripts/dot/lib/log.sh +6 -0
  222. package/scripts/dot/lib/platform.sh +22 -8
  223. package/scripts/dot/lib/ui.sh +145 -2
  224. package/scripts/dot/lib/utils.sh +6 -1
  225. package/scripts/git-hooks/pre-commit-audit.sh +1 -1
  226. package/scripts/git-hooks/pre-push +66 -3
  227. package/scripts/lib/secrets_provider.sh +32 -6
  228. package/scripts/ops/heal-chezmoi.sh +41 -6
  229. package/scripts/ops/rollback.sh +14 -0
  230. package/scripts/qa/powershell-contract.ps1 +95 -0
  231. package/scripts/security/check-disclosure-key-expiry.sh +110 -0
  232. package/scripts/security/lock-configs.sh +11 -2
  233. package/scripts/theme/merge-wallpaper.sh +4 -0
  234. package/scripts/theme/switch.sh +20 -10
  235. package/scripts/version-sync.sh +3 -0
  236. package/dot_config/atuin/config.toml +0 -40
  237. package/dot_local/bin/__pycache__/executable_dot-load-benchmark-ptycpython-312.pyc +0 -0
@@ -0,0 +1,182 @@
1
+ ---
2
+ render_with_liquid: false
3
+ ---
4
+
5
+ # Code Coverage
6
+
7
+ This page documents how coverage is measured, what the threshold is,
8
+ how to run it locally, and how to triage a regression. Closes the
9
+ docs slice of [#856](https://github.com/sebastienrousseau/dotfiles/issues/856).
10
+
11
+ ## Why pure bash xtrace (and not kcov)
12
+
13
+ The repo's primary code surface is bash (~140 shell files under
14
+ `scripts/`, hundreds more in `.chezmoitemplates/`). Standard
15
+ language-specific coverage tools (`coverage.py`, `cargo tarpaulin`,
16
+ `go cover`) don't apply.
17
+
18
+ We originally targeted [kcov](https://github.com/SimonKagstrom/kcov),
19
+ but kcov v43 on Ubuntu 24.04 + bash 5.2 cannot produce bash-script
20
+ coverage in any configuration we tried:
21
+
22
+ - Without bash debug symbols, kcov's ptrace backend fails to resolve
23
+ breakpoints and emits zero lines.
24
+ - With `bash-dbgsym` installed, kcov switches into C-binary tracking
25
+ mode and emits coverage entries for bash's internal C headers
26
+ (`ctype.h`, `stdio.h`, `wchar.h`) instead of the `.sh` files we
27
+ want measured.
28
+
29
+ Instead we use bash's own xtrace mechanism:
30
+
31
+ ```bash
32
+ PS4='+@COV@:${LINENO}:${BASH_SOURCE}:@ ' # encode line + source
33
+ BASH_ENV=/tmp/cov-setup.sh # `set -x` in every bash
34
+ bash test.sh 2>traces/test.trace # capture stderr per test
35
+ ```
36
+
37
+ `BASH_ENV` is inherited by every non-interactive bash invocation, so
38
+ subprocess `bash $SCRIPT_FILE` calls inside tests are also traced
39
+ automatically. The runner parses every trace for `:LINENO:FILE:`
40
+ matches and emits standard `lcov.info` that Codecov ingests natively.
41
+
42
+ ## Where it runs
43
+
44
+ | Surface | What runs |
45
+ |---|---|
46
+ | **PR + push to master** | `.github/workflows/coverage.yml` → `Coverage / kcov` job → uploads lcov.info to Codecov and fails the build below `MIN_COVERAGE_PCT` (currently `0`, ratcheted up each slice). |
47
+ | **Local dev** | `bash scripts/ci/run-coverage.sh` — works on Linux + macOS (xtrace is a bash primitive, no platform tools needed). |
48
+ | **macOS dev** | Supported. xtrace-based instrumentation runs on macOS bash 3.2+ and Homebrew bash 5.x. |
49
+
50
+ ## The current floor
51
+
52
+ `MIN_COVERAGE_PCT=0` in `.github/workflows/coverage.yml`. Slice 1
53
+ of [#883](https://github.com/sebastienrousseau/dotfiles/issues/883)
54
+ established the baseline at **~2.7% measured** (~613 of ~22 500 lines
55
+ across 231 files). Successive slices raised it; the current measured
56
+ value sits at **~47%**.
57
+
58
+ To tighten:
59
+
60
+ 1. Land a slice that bumps measured coverage.
61
+ 2. Wait until two-three Codecov runs report a stable value (no
62
+ per-PR jitter).
63
+ 3. Edit `MIN_COVERAGE_PCT` upward, ideally by ≤15 percentage points
64
+ per bump.
65
+ 4. Note the floor change in the commit message + this page.
66
+
67
+ ### Why not the 95% target from #883
68
+
69
+ The roadmap originally targeted ≥95% measured. After working through
70
+ all six slices, the achievable ceiling with xtrace-only instrumentation
71
+ is closer to **~50%** on this codebase. The remaining gap is structural,
72
+ not aspirational:
73
+
74
+ - **System-mutation surface** — large parts of the repo orchestrate
75
+ real OS state (`chezmoi apply`, `gpg`, `pass`/`age` keystores,
76
+ `gsettings`, signal-driven app reload, `git reset --hard`,
77
+ filesystem backups). Exercising these requires either a destroyable
78
+ sandbox (Docker / VM) or per-call mocks for every system tool.
79
+ - **Platform-gated branches** — every diagnostic and theme script
80
+ has Darwin / Linux / WSL forks. The xtrace runner only sees the
81
+ fork for the host it ran on; the others remain "uncovered"
82
+ forever from that one run's perspective. CI runs both macOS and
83
+ Linux but reports them separately.
84
+ - **Interactive UIs** — `fzf`, `gum`, `cmatrix`, `niri`, and the
85
+ Ghostty/Tmux reload helpers can't return to the test under
86
+ `bash -x` within a timeout budget. These are excluded at the
87
+ aggregator level.
88
+ - **Animated demo helpers** — same as interactive UIs.
89
+
90
+ `scripts/ci/run-coverage.sh` has a `SKIP_PATHS` set that removes
91
+ genuinely-untestable scripts from the lcov denominator. Within the
92
+ files that remain, individual mutation-only function bodies are
93
+ fenced with `# LCOV_EXCL_START` / `# LCOV_EXCL_STOP` and a one-line
94
+ rationale comment. Every exclusion line names the reason (`rm -rf
95
+ real $HOME`, `signals live apps`, `gpg keystore`, etc.) so future
96
+ maintainers can re-evaluate if the test infrastructure changes.
97
+
98
+ The graduated approach in this doc replaces the original 95% target.
99
+ The honest floor is the achievable one.
100
+
101
+ ## Running locally
102
+
103
+ ```bash
104
+ bash scripts/ci/run-coverage.sh # Linux or macOS
105
+
106
+ # Output:
107
+ # coverage/traces/<file>.trace — per-test xtrace logs
108
+ # coverage/lcov.info — lcov-format report Codecov ingests
109
+ ```
110
+
111
+ Open `coverage/lcov.info` in any lcov visualizer
112
+ (`genhtml coverage/lcov.info -o coverage/html`) for the per-file
113
+ heatmap.
114
+
115
+ ## Triaging a regression
116
+
117
+ When the `Coverage / kcov` PR check fails:
118
+
119
+ 1. Pull the workflow's `coverage-lcov` artifact (30-day retention).
120
+ 2. Compare against the previous master run by downloading its
121
+ `coverage-lcov` artifact too.
122
+ 3. Identify the file(s) where the line-coverage dropped.
123
+ 4. Either:
124
+ - Add tests covering the new code, or
125
+ - If the new code is provably unreachable in the test corpus
126
+ (e.g., a platform-specific branch only macOS tests exercise),
127
+ update the test suite to invoke it. Don't carve out global
128
+ exemptions — they accumulate.
129
+
130
+ ## Why not 100% yet
131
+
132
+ The previous workflow advertised "100% Coverage" without measuring
133
+ anything. Going from `0% measured` to `100% enforced` overnight is a
134
+ recipe for either:
135
+
136
+ - Suppressing the gate to ship anything ("just lower the threshold,
137
+ we'll fix it later"), or
138
+ - Padding the test suite with assertions that don't actually
139
+ exercise the code under test.
140
+
141
+ So this page documents a graduated approach: start with a measured
142
+ floor at 50%, ratchet upward as the test surface catches up to the
143
+ code surface. The previous aspirational "100%" labels in CI/job
144
+ names + branch-protection contexts have been renamed to match
145
+ reality (`Test / Unit Tests` instead of `Test / Unit Tests (100%
146
+ Coverage)`).
147
+
148
+ ## Codecov integration
149
+
150
+ Codecov (free OSS tier) is the canonical badge + PR-comment source.
151
+ The upload uses the
152
+ [`codecov/codecov-action`](https://github.com/codecov/codecov-action)
153
+ in tokenless mode (works for public repos out of the box; private
154
+ repos need `CODECOV_TOKEN`).
155
+
156
+ The Codecov GitHub App posts a status check on each PR with the
157
+ line-by-line diff coverage. Combine with this workflow's job-level
158
+ threshold to get two independent signals.
159
+
160
+ ## Excluded paths
161
+
162
+ `scripts/ci/run-coverage.sh` excludes:
163
+
164
+ - `tests/**` itself (don't measure coverage of the tests).
165
+ - `.git/`, `node_modules/`.
166
+ - Paths matched by `KCOV_EXCLUDE_PATTERN` (defaults reasonable).
167
+
168
+ Included paths (`KCOV_INCLUDE_PATH`):
169
+
170
+ - `scripts/`
171
+ - `.chezmoitemplates/functions/`
172
+ - `dot_local/bin/`
173
+
174
+ Adjust via the env vars at the top of `run-coverage.sh`.
175
+
176
+ ## References
177
+
178
+ - [Bash xtrace + PS4 + BASH_ENV docs](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html).
179
+ - [`scripts/ci/run-coverage.sh`](../../scripts/ci/run-coverage.sh).
180
+ - [`.github/workflows/coverage.yml`](../../.github/workflows/coverage.yml).
181
+ - Issue [#856](https://github.com/sebastienrousseau/dotfiles/issues/856) (closed) /
182
+ [#883](https://github.com/sebastienrousseau/dotfiles/issues/883) (coverage roadmap).
@@ -0,0 +1,107 @@
1
+ ---
2
+ render_with_liquid: false
3
+ ---
4
+
5
+ # Drift Detection & Remediation
6
+
7
+ This page documents how this repo detects drift between the chezmoi
8
+ source-of-truth and what's actually deployed on a host, how to read
9
+ the report, and how to remediate. Managed under
10
+ [#875](https://github.com/sebastienrousseau/dotfiles/issues/875).
11
+
12
+ ## What "drift" means here
13
+
14
+ Four distinct classes are tracked. The same `dot drift` command (and
15
+ the nightly CI workflow) surfaces all four.
16
+
17
+ | Class | Meaning | How to detect | Typical fix |
18
+ |---|---|---|---|
19
+ | **Managed drift** | A chezmoi-managed file's deployed copy differs from what a fresh `chezmoi apply` would produce. The standard case. | `chezmoi status` (M / MM / A / R rows) | Either update the source so apply is idempotent, or accept the deployed change and re-add. |
20
+ | **Untracked source** | The chezmoi source tree contains files git doesn't know about — usually in-progress local edits that haven't been committed. | `git -C <source-dir> ls-files --others --exclude-standard` | Commit, stash, or `.gitignore` the file. |
21
+ | **Orphan deployed** | A file under `$HOME` was previously chezmoi-managed but the source has since been deleted. Chezmoi no longer claims it, so a fresh apply leaves it behind silently. | Inventoried in `${XDG_STATE_HOME}/dotfiles/orphans` (populated by `dot heal` / `dot drift`) | `chezmoi remove --force` the path, or re-add the source if the file is still wanted. |
22
+ | **Stale source** | The deployed file is *newer* than its source. The next `chezmoi apply` would silently revert the user's hand-edit. Reverse-drift trap. | Compare mtimes for each managed target vs the resolved source-path | Promote the deployed change into the source (`chezmoi re-add`) or revert the deployed file. |
23
+
24
+ ## Reading the report
25
+
26
+ ```bash
27
+ dot drift # human-readable (uses the ui.sh formatting)
28
+ dot drift --json # single JSON object — used by the nightly workflow
29
+ dot drift --diff # also print `chezmoi diff` for managed drift
30
+ ```
31
+
32
+ JSON shape:
33
+
34
+ ```json
35
+ {
36
+ "managed_drift": 0,
37
+ "untracked_source": 0,
38
+ "orphan_deployed": 0,
39
+ "stale_source": 0,
40
+ "total": 0
41
+ }
42
+ ```
43
+
44
+ Exit code: `0` if every class is clean; `1` if any drift is found;
45
+ `2` if a prerequisite (chezmoi, git) is missing.
46
+
47
+ ## How the nightly check works
48
+
49
+ `.github/workflows/drift-detection.yml` runs `dot drift --json` against
50
+ a fresh checkout of `master` every day at 04:00 UTC. If `total != 0`
51
+ it opens (or updates) a tracking issue labelled
52
+ `type:chore + priority:medium` with the JSON summary, full
53
+ `chezmoi diff`, and `chezmoi status` attached as a workflow artifact.
54
+
55
+ The workflow itself ignores the failing exit code (`|| true`) for the
56
+ dashboard step — the actionable signal is the issue, not a red CI
57
+ indicator.
58
+
59
+ ## Force a local drift check
60
+
61
+ ```bash
62
+ dot drift # default — what you'd run before opening a PR
63
+ dot drift --json | jq '.' # for scripting / dashboards
64
+ ```
65
+
66
+ To force a full re-comparison after a tool upgrade or a force-apply:
67
+
68
+ ```bash
69
+ chezmoi apply --refresh-externals # refetch external sources
70
+ dot drift # re-scan
71
+ ```
72
+
73
+ ## Historical incidents
74
+
75
+ ### 2026-05-12 — `core.hooksPath` drift
76
+
77
+ The deployed `~/.gitconfig` contained a `hooksPath = ~/.git-templates/hooks`
78
+ line that wasn't in `dot_gitconfig.tmpl`. The line had been added
79
+ directly to the deployed file (manually, not via chezmoi), then sat
80
+ silently for weeks while the global `commit-msg` hook (at
81
+ `~/.config/git/hooks/commit-msg`) never fired — because `hooksPath`
82
+ was pointing at an empty directory. The result: every commit
83
+ authored on this machine silently shipped without the
84
+ `Assisted-by:` trailer mandated by `dot_claude/CLAUDE.md`.
85
+
86
+ Detection failure: no nightly drift check existed at the time.
87
+
88
+ Resolution: commit `f060683b` brought `hooksPath` into the chezmoi
89
+ template; this drift class is exactly what the new `stale_source`
90
+ signal catches going forward.
91
+
92
+ This incident is the canonical worked example for why the four-class
93
+ report exists rather than just `chezmoi status`.
94
+
95
+ ## Configuration surface
96
+
97
+ | Variable | Default | Purpose |
98
+ |---|---|---|
99
+ | `DOTFILES_DRIFT_SHOW_DIFF` | `0` | When `1`, append `chezmoi diff` (excluding scripts/install/tests) to the report. Equivalent to `--diff`. |
100
+
101
+ ## References
102
+
103
+ - `scripts/diagnostics/drift-dashboard.sh` — the dashboard itself.
104
+ - `.github/workflows/drift-detection.yml` — the nightly scanner.
105
+ - `tests/unit/diagnostics/test_drift_dashboard.sh` — JSON contract test.
106
+ - `dot heal` / `dot rollback` — drift remediation commands.
107
+ - Issue [#875](https://github.com/sebastienrousseau/dotfiles/issues/875).