the_local 0.3.0 → 0.4.1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -1
  3. data/CLAUDE.md +117 -0
  4. data/MIGRATING.md +478 -0
  5. data/PROVIDERS.md +62 -123
  6. data/README.md +20 -14
  7. data/lib/generators/the_local/provider_generator.rb +15 -90
  8. data/lib/the_local/agent.rb +3 -2
  9. data/lib/the_local/author.rb +43 -0
  10. data/lib/the_local/creators/develop.md +81 -0
  11. data/lib/the_local/creators/info.md +67 -0
  12. data/lib/the_local/creators/install.md +79 -0
  13. data/lib/the_local/disk_providers.rb +18 -10
  14. data/lib/the_local/format.rb +27 -0
  15. data/lib/the_local/front_matter.rb +26 -0
  16. data/lib/the_local/installer.rb +2 -2
  17. data/lib/the_local/interface.rb +26 -0
  18. data/lib/the_local/provider_check.rb +92 -0
  19. data/lib/the_local/rake.rb +14 -9
  20. data/lib/the_local/registry.rb +2 -30
  21. data/lib/the_local/trigger_writer.rb +1 -1
  22. data/lib/the_local/version.rb +1 -1
  23. data/lib/the_local.rb +1 -20
  24. data/the_local/agents/the_local-develop.md +48 -0
  25. data/the_local/agents/the_local-info.md +40 -0
  26. data/the_local/agents/the_local-install.md +49 -0
  27. data/the_local/interface.yml +21 -0
  28. metadata +16 -12
  29. data/lib/generators/the_local/templates/guide.md.tt +0 -41
  30. data/lib/generators/the_local/templates/reference.rb.tt +0 -17
  31. data/lib/generators/the_local/templates/the_local.rb.tt +0 -49
  32. data/lib/the_local/builder.rb +0 -57
  33. data/lib/the_local/reference/guide.md +0 -179
  34. data/lib/the_local/reference.rb +0 -16
  35. data/lib/the_local/the_local/agents/the_local-develop.md +0 -187
  36. data/lib/the_local/the_local/agents/the_local-info.md +0 -187
  37. data/lib/the_local/the_local/agents/the_local-install.md +0 -187
  38. data/lib/the_local/the_local.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16bc7ddfcc1ddab88976b10b156791a4e9a1985265d472517db9c56816d8022e
4
- data.tar.gz: 57c2ec4821bd1165037311dad0720cbcf1a753770052e0f88ae5a879f4e8c2f7
3
+ metadata.gz: c99c7f1c03fdf84c8e5180e9f3fe740a4712d16843b05a68be93c6e5f8d084cf
4
+ data.tar.gz: 0f54279ed111c33b424579dfdfe1d913cd9e1a0956ef906a1e24f28b4bf23151
5
5
  SHA512:
6
- metadata.gz: bf6c8dfab319c69dd82da9654cee64085c0aca38253ad29bdd37794887ff8b35136c5c1f5288e4beab1688d767b4f7f53f02ca115c7d0abcc40b3d34eeac2e2f
7
- data.tar.gz: 33a1549f7efedbcf20f3350f0c38224f03acf2865de2b7524037ba4c13889bf65b0ff75c2eff1d47a0ac6a0b4b57bdf57efcff4bd17b91a8c6a5bd1748258b8c
6
+ metadata.gz: 65973e9cc66580978399dc2dfed03dbea10c3c76837c04f37dd82aa32adeb77a7c86e9141b38fa7ce3a451cdab1ea7baf779f9e144fdd063f247e17493a78f6b
7
+ data.tar.gz: c717b4c90d5e9fc3e07c7efaab0a78dc529799f0d7d4592d49405342380a39ac8a3ed97c1cd3813c9df462073f3b45fbc946f94bd04b790d1f4e437a7a2f413d
data/CHANGELOG.md CHANGED
@@ -1,4 +1,39 @@
1
- ## [Unreleased]
1
+ ## [0.4.1] - 2026-07-31
2
+
3
+ - Installing no longer appends a blank line to the host's `CLAUDE.md` on every
4
+ run. Replacing the marked delegation section left the file's existing trailing
5
+ newline in place and the write added another on top of it, so the newlines
6
+ accumulated across `bundle`/refresh cycles.
7
+
8
+ ## [0.4.0] - 2026-07-31
9
+
10
+ - A provider gem ships **no Ruby and no guide**. Its committed
11
+ `the_local/agents/<gem>-{info,install,develop}.md` are authored by
12
+ `rake the_local:author`, which runs the_local's creators against the gem's
13
+ current code — one local at a time — and writes each from it. The creators live
14
+ inside the_local and are never installed into a host.
15
+ - A provider now declares its public interface in **`the_local/interface.yml`**:
16
+ the `scope` line, the entry points assigned to `install` and `develop`, and the
17
+ `sources` that define them. `rake the_local:author` refuses to run without it.
18
+ Deciding what is public is a human's call, made once and committed, instead of a
19
+ judgment the creators re-make on every run.
20
+ - The three locals **never overlap**: `install` hooks the gem into a consumer,
21
+ `develop` covers using it from consuming code, `info` is the read-only catchall.
22
+ One entry point belongs to exactly one local.
23
+ - `rake the_local:check` enforces that contract in both directions — every declared
24
+ entry point documented by its own local, nothing undeclared documented anywhere,
25
+ nothing documented by a local it wasn't declared for — on top of the existing
26
+ front-matter and section checks.
27
+ - Locals are **black-box docs**: they carry the gem's public interface (how, when,
28
+ where, what commands) and never reference its internals or send a reader into the
29
+ provider's source. Their facts come from the code, not from a README that could
30
+ be stale.
31
+ - The deterministic renderer is removed — no `the_local/guide.md`, no
32
+ `rake the_local:build`, no `ProviderBuild`/`Builder`. Install is unchanged: it
33
+ copies committed files off disk verbatim.
34
+ - `the_local:provider` wires the dependency and the Rakefile hook only; it no
35
+ longer scaffolds a guide. `the_local` is its own first provider, with a committed
36
+ manifest its locals are checked against.
2
37
 
3
38
  ## [0.3.0] - 2026-07-01
4
39
 
data/CLAUDE.md ADDED
@@ -0,0 +1,117 @@
1
+ <!-- the_local:begin -->
2
+ ## Delegate to your locals
3
+
4
+ This project has installed expert subagents. Before doing work yourself,
5
+ check whether a local owns it and delegate — never work from memory on
6
+ something a local covers:
7
+
8
+
9
+
10
+ See each agent's description for specifics.
11
+ <!-- the_local:end -->
12
+
13
+ <!-- the_local:process:begin -->
14
+ Read and follow this develop process for all work in this project. It is
15
+ also written verbatim to `develop_process_rules.md` — reference that file directly.
16
+
17
+ # Develop Process
18
+
19
+ The standard process for writing code across all projects. Default to these rules
20
+ unless a project explicitly overrides them.
21
+
22
+ ---
23
+
24
+ ## Diverging from this process
25
+
26
+ Read this process before starting work and follow it — it is the default for
27
+ every session. If a task genuinely calls for breaking one of these rules, do not
28
+ silently deviate: **PAUSE and ask for a one-time exception**, naming the rule and
29
+ why it should be set aside here. An exception is granted for that instance only —
30
+ it needs no doc or notes update — and then you continue. Do not treat a granted
31
+ exception as a standing change to the process.
32
+
33
+ ---
34
+
35
+ ## Test-Driven Development
36
+
37
+ TDD is the default for everything. Work one tiny cycle at a time:
38
+
39
+ 1. **Write one test that asserts one thing.**
40
+ 2. **Run it and watch it fail** — for the right reason. A test you never saw fail
41
+ proves nothing.
42
+ 3. **Write the minimum code to make it pass.**
43
+ 4. **Run the test and watch it pass.**
44
+ 5. **Commit.**
45
+ 6. Repeat with the next test.
46
+
47
+ One assertion per test. One test per commit cycle. No batching multiple behaviors
48
+ into a single test or a single commit.
49
+
50
+ ---
51
+
52
+ ## Commits
53
+
54
+ - A commit is normally **two files: the test file and the code file.**
55
+ - When implementing or updating an interface (e.g. a new controller endpoint) a
56
+ commit may touch more files (route + controller + view) — that is the minimal
57
+ coherent unit for that interface, and it is allowed.
58
+ - Keep each commit focused on the one behavior the test describes.
59
+
60
+ ---
61
+
62
+ ## What to Test
63
+
64
+ - **Test our own code only.**
65
+ - **Never test third-party code** — not a gem, not an API, not a framework. The
66
+ only test that may reference a dependency is one that asserts *our system is
67
+ correctly wired to it* (the integration seam), never the dependency's own
68
+ behavior.
69
+ - **Never test another interface inside a unit test.** A test covers one interface.
70
+ The single exception is the smoke integration test described below.
71
+
72
+ ---
73
+
74
+ ## Smoke Integration Test
75
+
76
+ When implementing an interface, write **one smoke integration test** that exercises
77
+ the interface end to end and proves the pieces are connected. This is the one place
78
+ where touching more than the unit under test is expected and correct.
79
+
80
+ ---
81
+
82
+ ## Pull Requests
83
+
84
+ - **Always work on a feature branch and open a PR.** Confirm the target branch
85
+ before any git operation (`git branch --show-current`).
86
+ - **Keep PRs small and manageable** — typically **no more than 8–10 files.**
87
+ - Keep the focus of a PR narrow. One concern per PR.
88
+ - **All tests pass before opening the PR.**
89
+ - **The linter and every other CI check pass before opening the PR.**
90
+ - Never start a new PR until the previous one is merged.
91
+
92
+ ---
93
+
94
+ ## Code Quality
95
+
96
+ - Follow Clean Code principles: small functions, clear names, no surprises.
97
+ - Follow SOLID principles. Readable by a human first.
98
+ - Keep it simple — no abstraction until a real need calls for it.
99
+ - Explicitly require libraries rather than assuming autoload.
100
+
101
+ ## Comments
102
+
103
+ - **Write self-documenting code, not comments.** Code should be clean and readable
104
+ on its own. Names — of classes, methods, variables, and partials — carry the intent.
105
+ - **A comment is a smell.** If you feel a comment is needed, the code is either built
106
+ wrong or needs refactoring (a clearer name, a smaller method, an extracted object or
107
+ partial) so the intent is obvious without prose. Follow SOLID and this resolves itself.
108
+ - Do not leave explanatory headers on classes/methods, inline "what this does" notes,
109
+ or section banners. Delete them and let the structure speak.
110
+ - Narrow exceptions, kept rare: a genuinely non-obvious *why* (a workaround for an
111
+ external bug, a legal/security constraint) and machine-readable annotations the
112
+ tooling requires (e.g. `rubocop:disable`). Prefer refactoring over a "why" comment
113
+ whenever you can.
114
+ <!-- the_local:process:end -->
115
+
116
+
117
+
data/MIGRATING.md ADDED
@@ -0,0 +1,478 @@
1
+ # Migrating a provider gem to the guide-based model
2
+
3
+ > **Superseded.** the_local no longer uses a guide. A provider now ships **no
4
+ > guide and no Ruby** — its committed `the_local/agents/*.md` are authored by the
5
+ > `the_local-author-*` creator agents, which read the gem's current code. To make
6
+ > a gem a provider, or to refresh a stale one, follow **[PROVIDERS.md](PROVIDERS.md)**,
7
+ > not this runbook. This file is kept only for gems still on the old
8
+ > `TheLocal.register` layout that need to reach a committed trio first; once they
9
+ > have one, re-author it with the creators.
10
+
11
+ This is a **deterministic runbook** for converting one provider gem from the old
12
+ `TheLocal.register` layout to the new guide-based one (the_local ≥ the version on
13
+ `main` after PR #72). Follow it top to bottom, **one gem at a time**. Every step
14
+ ends with a **VERIFY** gate; if a gate's actual output does not match the stated
15
+ expectation, **STOP** and resolve it before continuing — do not proceed on a
16
+ failed gate.
17
+
18
+ Do not batch gems. Do not skip verification. Do not invent steps.
19
+
20
+ ---
21
+
22
+ ## Two entry points
23
+
24
+ **Already on the guide model** (the gem has `the_local/guide.md` and no
25
+ `lib/**/the_local.rb`)? Its locals are rendering from the generic template and
26
+ the host agent is not routing to them. Branch first, update the engine, then skip
27
+ to **Step 6 — Author the locals' metadata** and run Steps 7-9:
28
+
29
+ ```bash
30
+ git checkout main && git pull --ff-only
31
+ git checkout -b chore/author-the-locals
32
+ bundle update the_local
33
+ bundle exec ruby -e 'require "the_local/guide"; puts "ok"' # => ok
34
+ ```
35
+
36
+ If that prints `LoadError` instead of `ok`, the bundled the_local predates
37
+ authored metadata — **STOP** and point the Gemfile at the_local `main`. Step 6's
38
+ VERIFY needs it.
39
+
40
+ **Still on the register model** (the gem has `lib/<path>/the_local.rb`)? Start at
41
+ the Preconditions and run the whole runbook top to bottom.
42
+
43
+ Either way you finish at the same place: an authored guide, a rebuilt trio, a
44
+ green drift test.
45
+
46
+ ---
47
+
48
+ ## What the migration changes
49
+
50
+ | Old (register-based) | New (guide-based) |
51
+ |---|---|
52
+ | `lib/<path>/the_local.rb` — `Companion.register!` block | *(deleted)* |
53
+ | `lib/<path>/reference.rb` — `Reference` loader | *(deleted)* |
54
+ | `lib/<path>/reference/guide.md` — the knowledge | **moved to** `the_local/guide.md` (gem root) |
55
+ | `c.agent description:`/`body:`/`scope:` — the authored metadata | **re-authored as** front matter in `the_local/guide.md` (Step 6) |
56
+ | `lib/<path>/the_local/agents/*.md` — committed locals | **rebuilt at** `the_local/agents/*.md` (gem root) |
57
+ | entrypoint `require`/`require_relative "<path>/the_local"` | *(deleted)* |
58
+ | Rakefile `require "<path>/the_local"` before `require "the_local/rake"` | *(deleted; keep the `the_local/rake` line)* |
59
+ | `test/**/companion_test.rb`, `test/**/reference_test.rb` | *(deleted; replaced by a drift test)* |
60
+
61
+ The gem carries **no Ruby** for the_local afterward — one `the_local/guide.md`
62
+ in, the rendered `the_local/agents/*.md` out.
63
+
64
+ `<path>` is the gem's lib subpath: `keystone_ui` for a plain gem,
65
+ `event_engine/subscribers` for a hyphenated gem. The commands below discover it;
66
+ never hardcode it.
67
+
68
+ ---
69
+
70
+ ## Preconditions (run once, at the gem root)
71
+
72
+ ```bash
73
+ cd <the gem's root> # the directory containing *.gemspec
74
+ git checkout main && git pull --ff-only # start clean
75
+ git checkout -b chore/the_local-guide-migration
76
+ ```
77
+
78
+ **VERIFY** you are in a provider gem on a fresh branch:
79
+
80
+ ```bash
81
+ ls *.gemspec # exactly one gemspec
82
+ git branch --show-current # chore/the_local-guide-migration
83
+ find lib -path '*/reference/guide.md' # exactly one path — the old guide
84
+ ```
85
+
86
+ - If there is **no** `*/reference/guide.md`, this gem is not an old-layout
87
+ provider (or already migrated). **STOP.**
88
+ - If there are **two or more**, **STOP** and report — the runbook assumes one.
89
+
90
+ Update to the new the_local so `rake the_local:build` uses the guide-based engine:
91
+
92
+ ```bash
93
+ bundle update the_local
94
+ ```
95
+
96
+ **VERIFY** the new engine is present:
97
+
98
+ ```bash
99
+ bundle exec ruby -e 'require "the_local/provider_build"; puts "ok"' # => ok
100
+ ```
101
+
102
+ If this prints an error instead of `ok`, the bundled the_local is too old.
103
+ **STOP** and fix the dependency (point the Gemfile at the_local `main`).
104
+
105
+ ---
106
+
107
+ ## Step 1 — Capture the current facts
108
+
109
+ ```bash
110
+ GUIDE=$(find lib -path '*/reference/guide.md') # lib/<path>/reference/guide.md
111
+ LIBPATH=$(dirname "$(dirname "$GUIDE")") # lib/<path>
112
+ GEM=$(basename ./*.gemspec .gemspec) # the gem name
113
+ echo "GUIDE=$GUIDE LIBPATH=$LIBPATH GEM=$GEM"
114
+ echo "-- committed locals today --"; ls "$LIBPATH/the_local/agents/"
115
+ ```
116
+
117
+ **VERIFY** the three variables are non-empty and `$LIBPATH/the_local/agents/`
118
+ lists the current `.md` files.
119
+
120
+ **DECISION — worker facet.** Look at the listed filenames:
121
+
122
+ - If they are `<gem>-info.md`, `<gem>-install.md`, `<gem>-develop.md` → normal
123
+ case, continue.
124
+ - If you see `<gem>-operate.md` (a CLI gem's worker) → the new model renders
125
+ **`develop`**, not `operate`. The migration **renames the worker**: the old
126
+ `operate` local disappears and a `develop` local replaces it. This is a
127
+ deliberate, visible change. Note it for the PR description and continue — the
128
+ old `-operate.md` is deleted in Step 3 and not rebuilt.
129
+
130
+ **GATE — is the guide actually finished?**
131
+
132
+ ```bash
133
+ grep -nE '^[[:space:]]*TODO:' "$GUIDE" || echo "no TODO markers"
134
+ grep -cE '^### (Interface|Recipe|Install|Conventions)' "$GUIDE" # expect 4
135
+ ```
136
+
137
+ - If there are **any** line-leading `TODO:` markers, or fewer than **4**
138
+ canonical sections, this gem's guide was never finished. The build gate will
139
+ reject it. **STOP.** This is *authoring* work (write the gem's real interface),
140
+ not mechanical migration — do it as a separate, deliberate task, then return.
141
+
142
+ ---
143
+
144
+ ## Step 2 — Move the guide to the gem root
145
+
146
+ ```bash
147
+ mkdir -p the_local
148
+ git mv "$GUIDE" the_local/guide.md
149
+ ```
150
+
151
+ **VERIFY**:
152
+
153
+ ```bash
154
+ test -f the_local/guide.md && echo "moved" # => moved
155
+ test -e "$GUIDE" && echo "STILL THERE — STOP" || echo "old path gone"
156
+ ```
157
+
158
+ Expect `moved` and `old path gone`.
159
+
160
+ ---
161
+
162
+ ## Step 3 — Delete the old Ruby and rendered locals
163
+
164
+ Delete the companion Ruby **first** — that way the require-cleanup in Step 4 sees
165
+ only the two lines that actually need removing, not the soft guard *inside* the
166
+ companion.
167
+
168
+ ```bash
169
+ git rm "$LIBPATH/the_local.rb" "$LIBPATH/reference.rb"
170
+ git rm -r "$LIBPATH/the_local/agents"
171
+ # reference/ dir held only the moved guide; remove it if now empty:
172
+ rmdir "$LIBPATH/reference" 2>/dev/null || true
173
+ # Delete the old drift/registration tests if the gem has them:
174
+ find test -name companion_test.rb -o -name reference_test.rb 2>/dev/null | xargs -r git rm
175
+ ```
176
+
177
+ **VERIFY** the old artifacts are gone:
178
+
179
+ ```bash
180
+ ls "$LIBPATH/the_local.rb" "$LIBPATH/reference.rb" 2>/dev/null \
181
+ && echo "OLD RUBY REMAINS — STOP" || echo "old ruby gone"
182
+ find "$LIBPATH" -path '*the_local/agents*' -name '*.md' | grep . \
183
+ && echo "OLD AGENTS REMAIN — STOP" || echo "old agents gone"
184
+ ```
185
+
186
+ Expect `old ruby gone` and `old agents gone`.
187
+
188
+ ---
189
+
190
+ ## Step 4 — Remove the companion require from the entrypoint and Rakefile
191
+
192
+ With the companion file already deleted, the gem still *requires* it from two
193
+ places (its entrypoint and its Rakefile). Both requires must go. **Keep**
194
+ `require "the_local/rake"` in the Rakefile.
195
+
196
+ ```bash
197
+ # Every line here is a companion require to delete (rake/builder are excluded):
198
+ grep -rn 'the_local"' lib Rakefile | grep -vE 'the_local/(rake|builder)"'
199
+ ```
200
+
201
+ Edit each file the command prints, deleting the line that requires the companion
202
+ (`require "<path>/the_local"` or `require_relative "<path>/the_local"`), plus any
203
+ now-orphaned comment directly above it. Do **not** delete `require "the_local/rake"`.
204
+
205
+ **VERIFY** no companion require remains, and the rake require survives:
206
+
207
+ ```bash
208
+ grep -rn 'the_local"' lib Rakefile | grep -vE 'the_local/(rake|builder)"' \
209
+ && echo "COMPANION REQUIRE REMAINS — STOP" || echo "entrypoint clean"
210
+ grep -q 'require "the_local/rake"' Rakefile && echo "rake require kept"
211
+ ```
212
+
213
+ Expect `entrypoint clean` and `rake require kept`.
214
+
215
+ ---
216
+
217
+ ## Step 5 — Make the gemspec ship `the_local/`
218
+
219
+ The committed locals now live at the gem root, so the gemspec must include them.
220
+ Check how this gem selects files:
221
+
222
+ ```bash
223
+ grep -n 'spec.files' ./*.gemspec
224
+ ```
225
+
226
+ - **`git ls-files`** based (ships every tracked file) → **no edit needed.** The
227
+ root `the_local/` ships automatically. Skip to VERIFY.
228
+ - **`Dir[...]` / `Dir.chdir { Dir[...] }`** based (an explicit allowlist, e.g.
229
+ `Dir["lib/**/*", "app/**/*"]` or `Dir["{app,config,db,lib}/**/*", ...]`) → the
230
+ root `the_local/` is **not** matched. Edit the glob to add `"the_local/**/*"`
231
+ to the file list.
232
+
233
+ Example edit (keystone_ui-style):
234
+
235
+ ```ruby
236
+ # before
237
+ spec.files = Dir["lib/**/*", "app/**/*"]
238
+ # after
239
+ spec.files = Dir["lib/**/*", "app/**/*", "the_local/**/*"]
240
+ ```
241
+
242
+ **VERIFY** the gemspec now includes the guide (once it exists on disk it must be
243
+ selectable). After Step 7 builds the agents, this is re-checked; for now just
244
+ confirm the edit is syntactically valid:
245
+
246
+ ```bash
247
+ ruby -e 'Gem::Specification.load(Dir["*.gemspec"].first)' && echo "gemspec loads"
248
+ ```
249
+
250
+ Expect `gemspec loads`. If it errors, **STOP** and fix the syntax.
251
+
252
+ ---
253
+
254
+ ## Step 6 — Author the locals' metadata
255
+
256
+ The register block carried each local's `description` and `body` plus the
257
+ provider's `scope`. **Moving the guide does not carry them over.** Without this
258
+ step every local renders from a generic template — `"Use PROACTIVELY for any
259
+ <gem> work"` — which the host agent only matches if the user already named the
260
+ gem, i.e. exactly when no local was needed. The local never fires and the host
261
+ answers generically. `rake the_local:build` refuses a guide whose scope is
262
+ unauthored, so this step is not optional.
263
+
264
+ **Recover the old wording first.** If this gem had a register block, git history
265
+ holds text that was already authored against the real gem — recover and improve
266
+ it rather than inventing new wording:
267
+
268
+ ```bash
269
+ git log --all --oneline -- "$LIBPATH/the_local.rb" | tail -1 # the commit that added it
270
+ git show <that-commit>:"$LIBPATH/the_local.rb" # scope, descriptions, bodies
271
+ ```
272
+
273
+ For a gem that never had one, investigate before writing: its gemspec, README,
274
+ public API, tests, and real call sites in consuming projects.
275
+
276
+ Add front matter at the very top of `the_local/guide.md`, above the `## Title`
277
+ line:
278
+
279
+ ```yaml
280
+ ---
281
+ scope: <domain> — <the tasks this gem owns>
282
+ locals:
283
+ info:
284
+ description: >-
285
+ Use to learn what <gem> offers — <its actual subjects>.
286
+ body: >-
287
+ What this local answers from, and that it changes nothing.
288
+ install:
289
+ description: >-
290
+ Use to add <gem> to a project and set it up correctly.
291
+ body: >-
292
+ The steps it follows, and what it must NOT do (e.g. companion gems).
293
+ develop:
294
+ description: >-
295
+ Use PROACTIVELY for <the real tasks, named> — MUST BE USED instead of
296
+ <the thing people hand-roll>.
297
+ body: >-
298
+ The ceremony that must never be skipped, and what is out of scope.
299
+ ---
300
+ ```
301
+
302
+ Each field answers one question:
303
+
304
+ | Field | Question | Why it matters |
305
+ |---|---|---|
306
+ | `scope` | What user-visible tasks does this gem own? | The one line the host's `CLAUDE.md` delegation rule names. |
307
+ | `description` | What would someone actually ask for? | **The routing surface.** The host agent matches a task against it to decide whether to delegate. |
308
+ | `body` | What ceremony must never be skipped? What is out of scope? | A standing instruction. Facts buried at line 90 of the reference are not instructions. |
309
+
310
+ You author only these. The renderer owns which locals exist, their `tools`, the
311
+ front-matter keys and their order, and the file's structure — do not add a fourth
312
+ local, do not set `tools`, do not invent keys.
313
+
314
+ **GATE — is the `develop` description actually routable?** Read it and answer:
315
+ *would this match a request that never says the gem's name?*
316
+
317
+ - `"Use PROACTIVELY for any event_engine work"` → **broken.** Names only the gem.
318
+ - `"Use PROACTIVELY for any EventEngine work — defining events, choosing
319
+ process_type, emitting, and keeping the committed schema in sync"` → routable.
320
+ Names the tasks.
321
+
322
+ If yours reads like the first, rewrite it before continuing. **This is the whole
323
+ point of the step**; a guide that passes the build gate but fails this question
324
+ ships a local nobody reaches.
325
+
326
+ **VERIFY** the front matter parses and is complete:
327
+
328
+ ```bash
329
+ bundle exec ruby -r the_local/guide -e '
330
+ g = TheLocal::Guide.new(File.read("the_local/guide.md"))
331
+ abort "UNAUTHORED SCOPE — STOP" if g.scope.to_s.strip.empty?
332
+ %w[info install develop].each do |n|
333
+ abort "#{n}: no description — STOP" if g.local(n)["description"].to_s.strip.empty?
334
+ end
335
+ abort "TODO markers remain — STOP" if g.scope.include?("TODO")
336
+ puts "authored"
337
+ '
338
+ ```
339
+
340
+ Expect `authored`. Anything else — fix it before Step 7.
341
+
342
+ ---
343
+
344
+ ## Step 7 — Build the committed locals
345
+
346
+ ```bash
347
+ bundle exec rake the_local:build
348
+ ```
349
+
350
+ **VERIFY** exactly the standard trio is rendered at the root, and the old worker
351
+ name is gone:
352
+
353
+ ```bash
354
+ ls the_local/agents/ # <gem>-info.md <gem>-install.md <gem>-develop.md
355
+ ls the_local/agents/*-operate.md 2>/dev/null && echo "OPERATE STILL PRESENT — STOP" || echo "no operate file"
356
+ ```
357
+
358
+ - Expect three files: `<gem>-info.md`, `<gem>-install.md`, `<gem>-develop.md`.
359
+ - If `rake the_local:build` **fails** with `incomplete guide(s)` → the guide has
360
+ a `TODO:` or is missing a section. Return to Step 1's GATE. **STOP.**
361
+
362
+ **VERIFY the build is idempotent** (a second build produces no diff):
363
+
364
+ ```bash
365
+ bundle exec rake the_local:build
366
+ git status --short the_local/agents/ # expect NO output
367
+ ```
368
+
369
+ Any output here means the build is non-deterministic — **STOP** and report.
370
+
371
+ ---
372
+
373
+ ## Step 8 — Add a drift test
374
+
375
+ Create a test that fails if a future edit to the guide isn't rebuilt. Adapt the
376
+ path and test framework to the gem (Minitest shown; the gem root is the dir with
377
+ the gemspec).
378
+
379
+ ```ruby
380
+ # test/the_local_drift_test.rb (or spec/the_local_drift_spec.rb)
381
+ # frozen_string_literal: true
382
+
383
+ require "test_helper"
384
+ require "the_local/provider_build"
385
+
386
+ class TheLocalDriftTest < Minitest::Test
387
+ def test_committed_agents_match_the_rendered_build
388
+ gem_root = File.expand_path("..", __dir__)
389
+ TheLocal::ProviderBuild.new(gem_root).agents.each do |agent|
390
+ assert_equal agent.to_markdown, File.read(agent.source_path)
391
+ end
392
+ end
393
+ end
394
+ ```
395
+
396
+ Adjust `File.expand_path("..", __dir__)` so it resolves to the gem root from the
397
+ test file's location (one `..` per directory level of the test file below root).
398
+
399
+ **VERIFY** the gem's whole suite passes:
400
+
401
+ ```bash
402
+ bundle exec rake test # or: bundle exec rspec
403
+ ```
404
+
405
+ Expect **0 failures, 0 errors**. If the drift test fails, the committed agents
406
+ don't match the build — re-run Step 7. If other tests fail because they
407
+ referenced the deleted companion/reference, delete or rewrite those assertions
408
+ (they tested removed code).
409
+
410
+ ---
411
+
412
+ ## Step 9 — Lint, review, commit, PR
413
+
414
+ ```bash
415
+ bundle exec rubocop # if the gem uses it — expect clean
416
+ git status --short # review every add/delete/modify
417
+ git diff --stat main
418
+ ```
419
+
420
+ **VERIFY** the diff contains only: the guide move, deleted companion/reference/old
421
+ agents, the entrypoint + Rakefile edits, the gemspec edit (unless git-ls-files),
422
+ the new `the_local/agents/*.md`, and the drift test. Anything else — **STOP** and
423
+ explain it before committing.
424
+
425
+ Commit in small, honest steps, then open a PR against the gem's `main`:
426
+
427
+ ```bash
428
+ git add -A
429
+ git commit -m "Migrate to the guide-based the_local provider model"
430
+ git push -u origin chore/the_local-guide-migration
431
+ gh pr create --base main --title "Migrate to the guide-based the_local provider model" --body "…"
432
+ ```
433
+
434
+ In the PR body, state plainly:
435
+ - The gem now ships no Ruby for the_local; one `the_local/guide.md` renders the
436
+ locals.
437
+ - **If this was an `operate` gem:** the worker local is renamed `operate` →
438
+ `develop`. Hosts that installed `<gem>-operate.md` keep a stale copy until they
439
+ re-run `the_local install`; note that a host re-sync is needed.
440
+
441
+ ---
442
+
443
+ ## The six gems and their specifics (ground truth)
444
+
445
+ Verified against the repos on disk. Use this to sanity-check what each gem's run
446
+ should look like — but still run every VERIFY gate; do not skip them.
447
+
448
+ **Check which entry point each gem needs before starting it** — a gem migrated
449
+ before the authoring step existed has an unauthored guide and needs Step 6
450
+ onward, not the whole runbook:
451
+
452
+ ```bash
453
+ test -f the_local/guide.md && ! ls lib/**/the_local.rb >/dev/null 2>&1 \
454
+ && echo "already migrated — start at Step 6" || echo "register model — start at Preconditions"
455
+ ```
456
+
457
+ | Gem | lib `<path>` | Worker | gemspec `files` | gemspec edit? | Old comp/ref tests | Notes |
458
+ |---|---|---|---|---|---|---|
459
+ | `citizen` | `citizen` | develop | `Dir.chdir { Dir["{app,config,db,lib}/**/*", …] }` | **yes** — add `"the_local/**/*"` | none | clean |
460
+ | `cs133` | `cs133` | develop | `git ls-files` | **no** | none | clean |
461
+ | `dash_kit` | `dash_kit` | develop | `Dir.chdir { Dir["{app,config,db,lib}/**/*", …] }` | **yes** | none | clean |
462
+ | `event_engine` | `event_engine` | develop | `Dir.chdir { Dir["{app,config,db,lib}/**/*", …] }` | **yes** | **companion_test + reference_test** — delete both | **already migrated (PR #238) with an unauthored guide — start at Step 6.** Its old register block holds the authored text to recover. |
463
+ | `event_engine-subscribers` | `event_engine/subscribers` | **operate** | `Dir.chdir { Dir["{app,config,db,lib}/**/*", …] }` | **yes** | none | **guide is all `TODO:` — unfinished scaffold. Step 1 GATE will STOP you: finish the guide first (authoring), or skip this gem.** |
464
+ | `keystone_ui` | `keystone_ui` | develop | `Dir["lib/**/*", "app/**/*"]` | **yes** | **companion_test + reference_test** — delete both | clean |
465
+
466
+ ---
467
+
468
+ ## Fail-safe summary
469
+
470
+ Stop and get help the moment any of these is true:
471
+
472
+ - A VERIFY gate's output doesn't match its stated expectation.
473
+ - The guide has `TODO:` markers or fewer than 4 canonical sections.
474
+ - `rake the_local:build` reports `incomplete guide(s)` or `unauthored scope`.
475
+ - A `develop` description names only the gem ("any `<gem>` work") — it will not route.
476
+ - A second build produces a diff (non-idempotent).
477
+ - The final `git diff --stat` shows files outside the expected set.
478
+ - The gem's test suite has failures you can't trace to deleted companion/reference code.