1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# Example rsmultigit config. This same text is available from a running binary
# via `rsmultigit config-example`, so to bootstrap:
#
# mkdir -p ~/.config/rsmultigit
# rsmultigit config-example > ~/.config/rsmultigit/config.toml
#
# The config location is fixed — there is no --config flag. Every rsmultigit
# command reads this file to decide which repositories to operate on.
#
# `repos` is a list of shell-expanded glob patterns that resolve to repo roots.
# Matches that aren't git repos are filtered out automatically.
#
# `[[check]]` blocks are consumed only by `rsmultigit check-same`, which verifies
# that a given path has identical content across a selection of repos.
#
# Selection primitives (per check rule):
# select — glob over repo names (required)
# exclude — glob of repo names to drop from the selection
# marker — only include repos that contain this file (relative to repo root)
# marker_absent — the inverse: drop repos that contain this file. This is how a
# repo opts out of an invariant from inside itself (e.g. a `.noci`
# file in a repo that deliberately has no CI) instead of being
# named in an `exclude` glob that lives far away from it.
# path — file inside each repo to compare (required)
# enabled — bool (default true). Disabled rules are silently skipped, but can
# still be forced on with `--checks <name...>` for ad-hoc checks.
# must_have — bool (default false). When true, every in-scope repo must contain
# `path`; missing files become rule violations (reported as "missing in:"
# and counted as mismatches). `check-same --fix-missing` will walk
# those violators interactively and create the file from a chosen
# seed group.
#
# A rule passes when every surviving repo's `path` hashes the same, *and*
# (when must_have = true) every in-scope repo contains the file.
# Repos that don't contain `path` are silently skipped when must_have = false.
#
# A rule that ends up matching no files at all fails ("no files matched") —
# that usually means a stale select/path. Pass `check-same --allow-empty` to
# let such rules pass as `ok (0 files)` instead.
= [
"~/git/*",
]
# ── shared dev-tooling configs across all repos ───────────────────────────────
[[]]
= "pylintrc"
= "*"
= ".pylintrc"
[[]]
= "gitignore"
= "*"
= ".gitignore"
[[]]
= "editorconfig"
= "*"
= ".editorconfig"
= true
[[]]
= "shellcheckrc"
= "*"
= ".shellcheckrc"
[[]]
= "jshintrc"
= "*"
= ".jshintrc"
[[]]
= "jshintignore"
= "*"
= ".jshintignore"
# rsconstruct.toml: the long-term goal is byte-identical in every repo
# (canonical copy: ~/git/rsconstruct.canonical.toml). Currently the files
# diverge by design (src_dirs pruned per repo, rsconstruct.local.toml merged
# in and removed on 2026-08-24), so this rule reports many groups; it stays
# as the tracker for the goal.
[[]]
= "rsconstruct-toml"
= "*"
= "rsconstruct.toml"
= false
[[]]
= "license"
= "*"
= "LICENSE"
= true
[[]]
= "readme-template"
= "*"
= "tera.templates/README.md.tera"
[[]]
= "requirements-thawed-template"
= "*"
= "templates/requirements.thawed.txt.mako"
[[]]
= "github-funding"
= "*"
= ".github/FUNDING.yml"
[[]]
= "sphinx-conf"
= "*"
= "tera.templates/sphinx/conf.py.tera"
[[]]
= "sphinx-index"
= "*"
= "sphinx/index.rst"
# Disabled: no repo has mdl configs any more (markdown linting moved to
# rumdl via rsconstruct), so these rules match nothing.
[[]]
= "mdlrc"
= "*"
= ".mdlrc"
= false
[[]]
= "mdl-style"
= "*"
= ".mdl.style.rb"
= false
# ── GCP repos ─────────────────────────────────────────────────────────────────
[[]]
= "gcp-gcloudignore"
= "gcp-*"
= ".gcp.conf"
= ".gcloudignore"
# Disabled: app.yaml is per-app deployment config. gcp-machines' copy carries
# env_variables (ADMIN_EMAILS/ACCESS_TOKEN) that its own src/main.py reads, so
# the three files legitimately differ and byte-identity is the wrong invariant.
[[]]
= "gcp-app-yaml"
= "gcp-*"
= ".gcp.conf"
= "app.yaml"
= false
[[]]
= "workflow-build-yml"
= "*"
= "rsconstruct.toml"
= ".github/workflows/build.yml"
# ── shared lua config for the tera renderer ──────────────────────────────────
# config/*.lua are read by rsconstruct's tera template renderer. personal.lua
# carries no per-repo values, so it is byte-identical by design. (project.lua
# and version.lua legitimately differ per repo and are deliberately not
# checked.)
[[]]
= "config-personal-lua"
= "*"
# veltzer.github.io carries a zero-byte config/personal.lua (and an empty
# project.lua) but has no tera.templates at all, so it never renders anything
# and the placeholder is excluded rather than filled with unused config.
= "veltzer.github.io"
= "config/personal.lua"
# yamllint config: one shared ruleset wherever YAML is linted.
[[]]
= "yamllint"
= "*"
= ".yamllint.yaml"
# The test package initializer is boilerplate, identical in every repo that
# has a tests/ package.
[[]]
= "tests-init"
= "*"
= "tests/__init__.py"
# ── rs* Rust repos: canonical files, synced from rsconstruct ──────────────────
[[]]
= "rs-release-info"
= "rs*"
= "docs/src/release-info.md"
= true
[[]]
= "rs-workflow-ci"
= "rs*"
= ".github/workflows/ci.yml"
= true
[[]]
= "rs-build-rs"
= "rs*"
= "build.rs"
= true
[[]]
= "rs-release-toml"
= "rs*"
= "release.toml"
= true