plumbbob 0.9.5 → 0.10.0

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 (142) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +87 -86
  3. package/dist/cli-core.js +8 -8
  4. package/dist/cli.js +1 -1
  5. package/dist/lib/agents.js +26 -26
  6. package/dist/lib/buildlog.js +11 -11
  7. package/dist/lib/buildlogsync.js +8 -8
  8. package/dist/lib/check.js +14 -14
  9. package/dist/lib/commitbody.js +72 -0
  10. package/dist/lib/commitmsg.js +7 -7
  11. package/dist/lib/git.js +44 -15
  12. package/dist/lib/intent.js +20 -19
  13. package/dist/lib/latch.js +16 -16
  14. package/dist/lib/orient.js +36 -17
  15. package/dist/lib/reentry.js +3 -3
  16. package/dist/lib/settings.js +16 -16
  17. package/dist/lib/sidecar.js +54 -54
  18. package/dist/lib/templates.js +1 -1
  19. package/dist/verbs/agent.js +25 -25
  20. package/dist/verbs/build.js +9 -9
  21. package/dist/verbs/check.js +6 -6
  22. package/dist/verbs/checkpoint.js +48 -47
  23. package/dist/verbs/doctor.js +27 -26
  24. package/dist/verbs/finish.js +27 -40
  25. package/dist/verbs/handoff.js +9 -8
  26. package/dist/verbs/init.js +9 -9
  27. package/dist/verbs/park.js +3 -3
  28. package/dist/verbs/recover.js +11 -11
  29. package/dist/verbs/revert.js +12 -12
  30. package/dist/verbs/spike.js +17 -17
  31. package/dist/verbs/start.js +49 -19
  32. package/dist/verbs/status.js +8 -8
  33. package/dist/verbs/turn.js +16 -16
  34. package/dist/verbs/use.js +4 -4
  35. package/node_modules/checkride/.claude-plugin/plugin.json +1 -1
  36. package/node_modules/checkride/CHANGELOG.md +401 -0
  37. package/node_modules/checkride/README.md +27 -9
  38. package/node_modules/checkride/dist/adapters.d.ts +12 -4
  39. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  40. package/node_modules/checkride/dist/adapters.js +21 -1
  41. package/node_modules/checkride/dist/adapters.js.map +1 -1
  42. package/node_modules/checkride/dist/agent-setup/cursor.d.ts +9 -1
  43. package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -1
  44. package/node_modules/checkride/dist/agent-setup/cursor.js +59 -1
  45. package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -1
  46. package/node_modules/checkride/dist/agent-setup/harness.d.ts +1 -1
  47. package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -1
  48. package/node_modules/checkride/dist/agent-setup/harness.js +10 -1
  49. package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -1
  50. package/node_modules/checkride/dist/agent-setup/scripts.d.ts +23 -0
  51. package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -1
  52. package/node_modules/checkride/dist/agent-setup/scripts.js +387 -82
  53. package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -1
  54. package/node_modules/checkride/dist/baseline/fallow.js +2 -2
  55. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +14 -5
  56. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  57. package/node_modules/checkride/dist/baseline/fingerprint.js +58 -5
  58. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  59. package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
  60. package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
  61. package/node_modules/checkride/dist/baseline/history.js +232 -0
  62. package/node_modules/checkride/dist/baseline/history.js.map +1 -0
  63. package/node_modules/checkride/dist/baseline/index.d.ts +4 -2
  64. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  65. package/node_modules/checkride/dist/baseline/index.js +2 -1
  66. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  67. package/node_modules/checkride/dist/baseline/store.d.ts +30 -7
  68. package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
  69. package/node_modules/checkride/dist/baseline/store.js +50 -16
  70. package/node_modules/checkride/dist/baseline/store.js.map +1 -1
  71. package/node_modules/checkride/dist/cli.d.ts +5 -1
  72. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  73. package/node_modules/checkride/dist/cli.js +98 -25
  74. package/node_modules/checkride/dist/cli.js.map +1 -1
  75. package/node_modules/checkride/dist/config.d.ts +33 -4
  76. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  77. package/node_modules/checkride/dist/config.js +21 -6
  78. package/node_modules/checkride/dist/config.js.map +1 -1
  79. package/node_modules/checkride/dist/digest/digest.js +1 -1
  80. package/node_modules/checkride/dist/doctor.d.ts +2 -0
  81. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  82. package/node_modules/checkride/dist/doctor.js +39 -1
  83. package/node_modules/checkride/dist/doctor.js.map +1 -1
  84. package/node_modules/checkride/dist/gate.d.ts +49 -4
  85. package/node_modules/checkride/dist/gate.d.ts.map +1 -1
  86. package/node_modules/checkride/dist/gate.js +143 -16
  87. package/node_modules/checkride/dist/gate.js.map +1 -1
  88. package/node_modules/checkride/dist/init.d.ts +8 -1
  89. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  90. package/node_modules/checkride/dist/init.js +68 -7
  91. package/node_modules/checkride/dist/init.js.map +1 -1
  92. package/node_modules/checkride/dist/node-pin.d.ts +1 -1
  93. package/node_modules/checkride/dist/node-pin.js +1 -1
  94. package/node_modules/checkride/dist/orchestrator.d.ts +13 -0
  95. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  96. package/node_modules/checkride/dist/orchestrator.js +118 -13
  97. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  98. package/node_modules/checkride/dist/pm/index.d.ts +1 -0
  99. package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
  100. package/node_modules/checkride/dist/pm/index.js.map +1 -1
  101. package/node_modules/checkride/dist/pm/launch.d.ts +23 -2
  102. package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -1
  103. package/node_modules/checkride/dist/pm/launch.js +16 -0
  104. package/node_modules/checkride/dist/pm/launch.js.map +1 -1
  105. package/node_modules/checkride/dist/pm/translate.js +1 -1
  106. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  107. package/node_modules/checkride/dist/qa/mutation.d.ts +4 -4
  108. package/node_modules/checkride/dist/qa/mutation.js +4 -4
  109. package/node_modules/checkride/dist/recover.d.ts +48 -0
  110. package/node_modules/checkride/dist/recover.d.ts.map +1 -0
  111. package/node_modules/checkride/dist/recover.js +220 -0
  112. package/node_modules/checkride/dist/recover.js.map +1 -0
  113. package/node_modules/checkride/dist/smoke.d.ts +1 -1
  114. package/node_modules/checkride/dist/smoke.js +1 -1
  115. package/node_modules/checkride/dist/snippets.d.ts +1 -1
  116. package/node_modules/checkride/package.json +3 -2
  117. package/node_modules/checkride/schema/checkride.config.schema.json +9 -1
  118. package/node_modules/checkride/skills/check/SKILL.md +9 -1
  119. package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
  120. package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
  121. package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
  122. package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
  123. package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
  124. package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
  125. package/node_modules/checkride/templates/shared/vale.ini +34 -0
  126. package/package.json +10 -9
  127. package/skills/build/SKILL.md +69 -64
  128. package/skills/doctor/SKILL.md +11 -11
  129. package/skills/finish/SKILL.md +20 -19
  130. package/skills/harvest/SKILL.md +13 -12
  131. package/skills/park/SKILL.md +10 -10
  132. package/skills/plan/SKILL.md +63 -54
  133. package/skills/recover/SKILL.md +6 -6
  134. package/skills/refine/SKILL.md +18 -16
  135. package/skills/revert/SKILL.md +6 -6
  136. package/skills/spike/SKILL.md +9 -9
  137. package/skills/status/SKILL.md +5 -5
  138. package/skills/step/SKILL.md +17 -12
  139. package/skills/verify/SKILL.md +45 -40
  140. package/templates/build-log.md +7 -7
  141. package/templates/intent.md +49 -39
  142. package/templates/spike-report.md +10 -10
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plumbbob",
3
3
  "displayName": "PlumbBob",
4
- "version": "0.9.5",
4
+ "version": "0.10.0",
5
5
  "description": "Attention-first build process — driver skills + a CLI that keep you the decider: guidance on the work, a latch on the record.",
6
6
  "author": { "name": "Rob McLarty", "email": "hello@robmclarty.com", "url": "https://robmclarty.com" },
7
7
  "homepage": "https://github.com/robmclarty/plumbbob#readme",
package/README.md CHANGED
@@ -10,20 +10,20 @@
10
10
  <a href="https://robmclarty.github.io/plumbbob/api.html">API reference</a>
11
11
  </p>
12
12
 
13
- PlumbBob is a Claude Code plugin — thirteen skills and a small CLI — that runs
13
+ PlumbBob is a Claude Code plugin (thirteen skills and a small CLI) that runs
14
14
  LLM-assisted coding as a loop you control. You and the model settle the plan in a file
15
15
  *before* any code; then the model builds one small step at a time and **stops after
16
16
  each one**, waiting for you to read the diff and approve it before anything is
17
- committed. It's built for the small-to-medium work that fills most days — a feature, a
18
- bug, a refactor — where a fully autonomous build is overkill but freestyle prompting
17
+ committed. It's built for the small-to-medium work that fills most days (a feature, a
18
+ bug, a refactor) where a fully autonomous build is overkill but freestyle prompting
19
19
  leaves you dragged behind the model, reacting instead of deciding.
20
20
 
21
21
  > Establish *plumb* before you build. The LLM is a hand, not a head.
22
22
 
23
23
  Its one law is **vibe to execute, never vibe to decide**: the human owns every
24
24
  decision, the LLM owns the labor, and the boundary between them is a **pause you
25
- advance**, not a wall that refuses you. The *why* behind that — attention as the scarce
26
- resource — is in [`docs/attention-first-development.md`](docs/attention-first-development.md).
25
+ advance**, not a wall that refuses you. The *why* behind that (attention as the scarce
26
+ resource) is in [`docs/attention-first-development.md`](docs/attention-first-development.md).
27
27
  PlumbBob is the layer below [Ridgeline](https://github.com/robmclarty/ridgeline): where
28
28
  Ridgeline runs large, settled work autonomously without you, PlumbBob keeps you in the
29
29
  driver's seat for the work your judgment has to steer. This repository was built using
@@ -32,24 +32,24 @@ PlumbBob itself, running its own loop.
32
32
  ## What a prompt can't replicate
33
33
 
34
34
  A system prompt can *ask* a model to plan first and stop for review. What it can't do is
35
- **hold the line when the model doesn't** — and that mechanical half is what PlumbBob adds
35
+ **hold the line when the model doesn't**, and that mechanical half is what PlumbBob adds
36
36
  under the skills. Five deterministic edges, enforced by the CLI and git rather than by the
37
37
  model's goodwill:
38
38
 
39
39
  - **A gate that refuses red.** `checkpoint` won't record a step while the check gate is
40
- red, and `start` refuses a dirty tree — the [checkride](https://www.npmjs.com/package/checkride)
40
+ red, and `start` refuses a dirty tree: the [checkride](https://www.npmjs.com/package/checkride)
41
41
  run (types, lint, tests, dead code, docs) has to pass before a step lands.
42
42
  - **One SHA per verified step.** Every approved step is its own git commit against a
43
- recorded baseline — a ledger of checkpoints, not one undifferentiated diff at the end.
43
+ recorded baseline: a ledger of checkpoints, not one undifferentiated diff at the end.
44
44
  - **A preservation-aware revert.** `/plumbbob:revert` resets exactly to a recorded checkpoint
45
45
  SHA while keeping your plan and notes intact, and restores *only* SHAs the loop wrote.
46
- - **A build record that rides the PR.** The tracked `builds/<slug>/` folder — intent, log,
47
- checkpoints, report — merges into `main` with the branch instead of dying with the
46
+ - **A build record that rides the PR.** The tracked `builds/<slug>/` folder (intent, log,
47
+ checkpoints, report) merges into `main` with the branch instead of dying with the
48
48
  worktree; the archive never destroys.
49
49
  - **The approval latch.** `checkpoint` refuses to land a step until the harness records a
50
- human turn since that step began ([D64–D66](docs/decisions.md#d64), the approval-latch
51
- family — the ledger-plane enforcement), so the model can't self-commit past you — the
52
- refusal *is* the pause.
50
+ human turn since that step began ([D64 (approval-latch)](docs/decisions.md#d64)–[D66
51
+ (oob-commits-surfaced)](docs/decisions.md#d66), the ledger-plane enforcement), so the
52
+ model can't self-commit past you: the refusal *is* the pause.
53
53
 
54
54
  The planning surface is the on-ramp; these five are the floor under it. And the latch's
55
55
  payoff is [measured, not asserted](#why-not-just), not just claimed. **Guidance on the
@@ -64,7 +64,7 @@ work, a latch on the record.**
64
64
  Everything the model must stay true to lives in one file, the build's `intent.md`
65
65
  (under `.plumbbob/builds/<slug>/`), written before any code: the **Frame** (the problem, what done looks like, what you're
66
66
  explicitly *not* doing), the **Decisions** and **Constraints** (the settled calls, each
67
- with its *because*), and the **Steps** — a numbered list where every step carries a
67
+ with its *because*), and the **Steps**: a numbered list where every step carries a
68
68
  **done-when** (a checkable finish line, ideally a test) and a **seam** (the files it's
69
69
  expected to touch).
70
70
 
@@ -76,16 +76,16 @@ expected to touch).
76
76
  /plumbbob:finish report what shipped and why, final commit, clear (once)
77
77
  ```
78
78
 
79
- Nothing is locked and nothing refuses you — the loop does a step's labor, pulls up to
79
+ Nothing is locked and nothing refuses you: the loop does a step's labor, pulls up to
80
80
  the line, and waits for you to advance it. **You are the clock.**
81
81
 
82
82
  ## Install
83
83
 
84
- PlumbBob installs **once, globally** — like `gh` or your dotfiles. There are two ways;
84
+ PlumbBob installs **once, globally** (like `gh` or your dotfiles). Two ways work;
85
85
  pick one (both register a Claude Code plugin named `plumbbob`, so running both
86
86
  collides).
87
87
 
88
- **Marketplace plugin** — Claude Code installs the published `plumbbob` package for you
88
+ **Marketplace plugin**: Claude Code installs the published `plumbbob` package for you
89
89
  (skills, hook, and the CLI on PATH), so you run no `npm i -g` and no `init`:
90
90
 
91
91
  ```text
@@ -93,7 +93,7 @@ collides).
93
93
  /plugin install plumbbob@robmclarty
94
94
  ```
95
95
 
96
- **npm global + `init`** — installs the CLI, then links the skills and hook into Claude
96
+ **npm global + `init`**: installs the CLI, then links the skills and hook into Claude
97
97
  Code in place:
98
98
 
99
99
  ```sh
@@ -102,15 +102,15 @@ plumbbob init # link it into Claude Code; --uninstall to undo
102
102
  ```
103
103
 
104
104
  Restart Claude Code (or `/reload-plugins`) to activate, then run `plumbbob doctor`
105
- (or `/plumbbob:doctor` in-session) to confirm the wiring. The full guide — namespacing,
106
- per-project sessions, the agent-neutral roadmap — is in
105
+ (or `/plumbbob:doctor` in-session) to confirm the wiring. The full guide (namespacing,
106
+ per-project sessions, the agent-neutral roadmap) is in
107
107
  [`docs/install.md`](docs/install.md).
108
108
 
109
109
  ## Your first session
110
110
 
111
111
  In Claude Code, inside any git repo with a clean tree:
112
112
 
113
- 1. **Plan.** Fire `/plumbbob:plan` and give it whatever you have — nothing (it interviews
113
+ 1. **Plan.** Fire `/plumbbob:plan` and give it whatever you have: nothing (it interviews
114
114
  you), a rough line (`/plumbbob:plan rate-limit POST /login, 5/min/IP, return 429`), or a
115
115
  path to a spec file. Together you fill the build's `intent.md`. No code is written
116
116
  yet.
@@ -121,17 +121,17 @@ In Claude Code, inside any git repo with a clean tree:
121
121
  PAUSE — read the diff as an editor. Approve to checkpoint, or send fixes.
122
122
  ```
123
123
 
124
- (The check gate is [checkride](https://www.npmjs.com/package/checkride) — one
124
+ (The check gate is [checkride](https://www.npmjs.com/package/checkride), one
125
125
  run across the tools your repo already configures: types, lint, tests, dead
126
126
  code, docs. To gate through your own command instead, set the `"check"` key in
127
- `.plumbbob/settings.json`, e.g. `"check": "npm test"`.)
128
- 3. **Approve** — or send fixes. On your OK it commits the step as a checkpoint, marks
129
- it done, and returns to the boundary. Fire `/plumbbob:build` again for the next step —
127
+ `.plumbbob/settings.json`, for example `"check": "npm test"`.)
128
+ 3. **Approve**, or send fixes. On your OK it commits the step as a checkpoint, marks
129
+ it done, and returns to the boundary. Fire `/plumbbob:build` again for the next step:
130
130
  re-firing it *is* the clock tick. Whenever you lose the thread, `/plumbbob:status` shows
131
131
  where you are and names the next move.
132
132
  4. **Finish.** When the last step is done, `/plumbbob:finish` writes a report of what shipped
133
133
  and why into the build's tracked folder, makes the final commit, and clears the slate
134
- for the next goal. The folder rides the branch into the PR — the build's record merges
134
+ for the next goal. The folder rides the branch into the PR: the build's record merges
135
135
  into `main` instead of dying with the worktree.
136
136
 
137
137
  A mid-build "ooh, what if…" never derails the step: `/plumbbob:park` captures it to a list
@@ -141,23 +141,23 @@ end, with the real output at every stage, is in
141
141
 
142
142
  ## The skills
143
143
 
144
- You drive the whole loop from your IDE with `/plumbbob:*` skills — no step numbers to
144
+ You drive the whole loop from your IDE with `/plumbbob:*` skills: no step numbers to
145
145
  remember, no raw CLI to type. Every skill is `disable-model-invocation`, so *you* fire
146
146
  every move, and `/plumbbob:status` always names your next one. (Claude Code namespaces
147
147
  them under the plugin, so the command is `/plumbbob:plan`. The bare `/plan` reaches it
148
- too — but only where nothing else owns that name, and four of the thirteen share one with
148
+ too, but only where nothing else owns that name, and four of the thirteen share one with
149
149
  a Claude Code built-in, so these docs write the full form.)
150
150
 
151
- **The happy path** — the three moves every session makes; many sessions need
151
+ **The happy path**: the three moves every session makes; many sessions need
152
152
  nothing else:
153
153
 
154
154
  | Skill <img alt="" width="110" height="1"> | Does |
155
155
  |------------------------------------|------|
156
- | `/plumbbob:plan` | plan the whole goal — open the session and author intent's Frame, Decisions, Constraints, **and all Steps** |
157
- | `/plumbbob:build` | implement the next planned step, then verify it to the pause — `--auto` self-approves and chains to done; a range like `1-3` self-approves through step 3, then pauses |
158
- | `/plumbbob:finish` | finish up — write the report, make the final commit, clear for a fresh goal |
156
+ | `/plumbbob:plan` | plan the whole goal: open the session and author intent's Frame, Decisions, Constraints, **and all Steps** |
157
+ | `/plumbbob:build` | implement the next planned step, then verify it to the pause: `--auto` self-approves and chains to done; a range like `1-3` self-approves through step 3, then pauses |
158
+ | `/plumbbob:finish` | finish up: write the report, make the final commit, clear for a fresh goal |
159
159
 
160
- **Plan-shaping moves** — optional, for when the plan needs work mid-flight:
160
+ **Plan-shaping moves**: optional, for when the plan needs work mid-flight:
161
161
 
162
162
  | Skill <img alt="" width="110" height="1"> | Does |
163
163
  |------------------------------------|------|
@@ -165,17 +165,17 @@ nothing else:
165
165
  | `/plumbbob:refine` | attack the frame for holes, or repair a drifted plan |
166
166
  | `/plumbbob:spike` | throwaway worktree experiment for a fork the plan can't settle |
167
167
 
168
- **Helpers** — orient, verify, recover, diagnose:
168
+ **Helpers**: orient, verify, recover, diagnose:
169
169
 
170
170
  | Skill <img alt="" width="110" height="1"> | Does |
171
171
  |------------------------------------|------|
172
- | `/plumbbob:status` | orient — where you are, the next step's done-when and seam, and the next move |
173
- | `/plumbbob:verify` | the tick, standalone — check → self-review → validate → **PAUSE** → checkpoint, for a diff `/plumbbob:build` didn't write |
174
- | `/plumbbob:revert` | rewind — `git reset --hard` to a recorded checkpoint |
172
+ | `/plumbbob:status` | orient: where you are, the next step's done-when and seam, and the next move |
173
+ | `/plumbbob:verify` | the tick, standalone: check → self-review → validate → **PAUSE** → checkpoint, for a diff `/plumbbob:build` didn't write |
174
+ | `/plumbbob:revert` | rewind: `git reset --hard` to a recorded checkpoint |
175
175
  | `/plumbbob:recover` | reconcile the session's own state when the dashboard looks wrong |
176
176
  | `/plumbbob:doctor` | check the install from inside a session |
177
177
 
178
- **Capture** — the park/harvest loop for mid-build ideas:
178
+ **Capture**: the park/harvest loop for mid-build ideas:
179
179
 
180
180
  | Skill <img alt="" width="110" height="1"> | Does |
181
181
  |------------------------------------|------|
@@ -187,27 +187,27 @@ All thirteen, with inputs and effects, are in
187
187
 
188
188
  Under the skills ships a lean `plumbbob` CLI (the mechanical verbs the
189
189
  skills shell out to), one session-gated post-edit hook (non-blocking lint feedback in
190
- flow), and a `.plumbbob/` sidecar you can open and edit by hand at any time — a tracked
190
+ flow), and a `.plumbbob/` sidecar you can open and edit by hand at any time: a tracked
191
191
  `builds/<slug>/` folder per build (`intent.md`, `build-log.md`, `checkpoints`, `report.md`)
192
192
  that rides the branch into the PR, plus an untracked control plane (`settings.local.json`,
193
193
  the session sentinel, the in-flight markers).
194
194
 
195
- **`/plumbbob:build` is the default engine, not the only one.** It's one executor — the
196
- loop doesn't care who writes the code. Implement a step by hand — or vibe it in
197
- another session, or in another harness entirely — and run `/plumbbob:verify` instead:
195
+ **`/plumbbob:build` is the default engine, not the only one.** It's one executor: the
196
+ loop doesn't care who writes the code. Implement a step by hand (or vibe it in
197
+ another session, or in another harness entirely) and run `/plumbbob:verify` instead:
198
198
  same checks, same pause, same checkpoint. It reads the *diff, not the author*.
199
199
 
200
200
  **Bring your own agents.** Because the executor is author-blind, you can plug your
201
201
  own in. A **user-authored agent** is any executable that speaks one small JSON
202
202
  envelope; drop it under `.plumbbob/agents/`, bind it to a step's `before`, `build`,
203
203
  or `after` slot in the build's `harness.json`, and PlumbBob runs it through the same
204
- pause — with **no way for it to advance the loop** (checkride still gates, you're
204
+ pause, with **no way for it to advance the loop** (checkride still gates, you're
205
205
  still the clock). The full contract for authors, with a copy-paste example, is in
206
206
  [`docs/agents.md`](docs/agents.md).
207
207
 
208
208
  ## Why "PlumbBob"
209
209
 
210
- The name is the method. A plumb bob is a weight on a string — gravity pulls it into a
210
+ The name is the method. A plumb bob is a weight on a string; gravity pulls it into a
211
211
  perfectly straight vertical line, the builder's oldest reference for *true*: the fixed
212
212
  mark you hold the work against so it never drifts out of square. PlumbBob hangs your
213
213
  **intent** as that line and keeps every step aligned to it, so the build stays plumb
@@ -217,90 +217,91 @@ with what you decided instead of wandering off behind the model.
217
217
 
218
218
  The questions a skeptic should ask, answered straight.
219
219
 
220
- **…use plan mode?** Plan mode is the same instinct — decide before executing —
220
+ **…use plan mode?** Plan mode is the same instinct (decide before executing),
221
221
  scoped to one session. The plan lives in the chat, so it fades with the context
222
222
  window, and the approval gates the *start* of the work, not each increment of it.
223
223
  PlumbBob puts the plan in a file in your repo that survives compaction, restarts,
224
- and next week; moves the approval to **every step** — each checked, reviewed
225
- against the plan, and committed as its own checkpoint, so a wrong turn costs one
224
+ and next week; moves the approval to **every step** (each checked, reviewed
225
+ against the plan, and committed as its own checkpoint), so a wrong turn costs one
226
226
  step, not the afternoon; and records the *whys* alongside the decisions, so the
227
227
  archive can still answer "what did we decide, and why" long after the session is
228
- gone. The two compose fine: plan mode — or anything else — can produce a step's
228
+ gone. The two compose fine: plan mode (or anything else) can produce a step's
229
229
  diff, and `/plumbbob:verify` reads it like any other.
230
230
 
231
231
  **…use git and a TODO file?** That's the honest skeleton, and if your discipline
232
232
  holds by willpower alone, it may be all you need. What the CLI adds is the
233
- [mechanical half of the discipline](#what-a-prompt-cant-replicate) — the five edges up
234
- top — so willpower is spent only at the pause. The one not on that list: a status
233
+ [mechanical half of the discipline](#what-a-prompt-cant-replicate) (the five edges up
234
+ top), so willpower is spent only at the pause. The one not on that list: a status
235
235
  dashboard re-injected into every skill, so the model re-orients on each move without you
236
236
  re-explaining the state.
237
237
 
238
- **…wait for a vendor to ship this natively?** They're converging on it — plan
239
- modes, spec kits, native checkpoints — and that convergence is evidence *for* the
238
+ **…wait for a vendor to ship this natively?** They're converging on it (plan
239
+ modes, spec kits, native checkpoints), and that convergence is evidence *for* the
240
240
  premise. PlumbBob is deliberately thin against that future: the artifacts are
241
241
  plain markdown and ordinary git commits in your own repo, the CLI is lean
242
242
  (node builtins plus one deliberate dependency), and nothing locks you in. The
243
- investment is small — thirteen
244
- skills over a plain-markdown sidecar, learnable in an afternoon — and what you're
243
+ investment is small (thirteen
244
+ skills over a plain-markdown sidecar, learnable in an afternoon), and what you're
245
245
  actually learning is the method: decisions before code, one verified step at a
246
246
  time, capture instead of chase. That transfers to whatever tool wins. If
247
247
  something better ships, you walk away with your archives and your habits intact.
248
248
 
249
- **…admit the pause is unenforceable?** On the **work** plane, yes — on purpose
249
+ **…admit the pause is unenforceable?** On the **work** plane, yes, on purpose
250
250
  ([D10 (pause-not-lock)](docs/decisions.md#d10)/[D13 (no-edit-guards)](docs/decisions.md#d13) in
251
251
  [`docs/decisions.md`](docs/decisions.md)): a hard lock on every edit buys ritual, not
252
252
  control, because a determined model routes around it and a denial mid-thought leaves no
253
253
  legal move. But the **record** is a different plane, and there the tick *is* latched
254
- ([D64–D66](docs/decisions.md#d64), the approval-latch family): `checkpoint` refuses to land a step until the harness
255
- records a human turn since that step began, so the agent cannot self-commit past you —
254
+ ([D64 (approval-latch)](docs/decisions.md#d64)–[D66 (oob-commits-surfaced)](docs/decisions.md#d66)): `checkpoint` refuses to land a step until the harness
255
+ records a human turn since that step began, so the agent cannot self-commit past you:
256
256
  the refusal simply *is* the pause. It extends the [deterministic edges above](#what-a-prompt-cant-replicate)
257
257
  to the one boundary the product is named after, while leaving human what must be human:
258
258
  you, reading the diff. And when a raw commit does slip the ledger, `status` says so and the
259
- checkpoint record makes recovery one command — cheap recovery, not a cage, is the control
259
+ checkpoint record makes recovery one command; cheap recovery, not a cage, is the control
260
260
  that matters.
261
261
 
262
262
  > The latch's payoff is measured, not asserted: the skill-eval harness
263
- > ([`test/evals/`](test/evals/)) sweeps this loop headless — prose-only baseline vs. the
264
- > shipped latch, N=5 per contract, every assertion a mechanical read of git state — and
263
+ > ([`test/evals/`](test/evals/)) sweeps this loop headless (prose-only baseline vs. the
264
+ > shipped latch, N=5 per contract, every assertion a mechanical read of git state) and
265
265
  > commits each receipt under [`docs/evals/`](docs/evals/). The first sweep
266
- > ([opus, 2026-07-11](docs/evals/2026-07-11.md)) closed the route the latch owns —
267
- > *no checkpoint over a red check under pressure* went from **2/5 prose-only to 5/5
268
- > latched** — and named, rather than papered over, the two honest gaps it surfaced: park
266
+ > ([opus, 2026-07-11](docs/evals/2026-07-11.md)) closed the route the latch owns
267
+ > (*no checkpoint over a red check under pressure* went from **2/5 prose-only to 5/5
268
+ > latched**) and named, rather than papered over, the two honest gaps it surfaced: park
269
269
  > capture at 0/5, and adversarial pressure flipping a standing `auto` grant a model could
270
270
  > write into settings, a legal side door. The re-sweep
271
- > ([opus, 2026-07-18](docs/evals/2026-07-18.md), plumbbob 0.8.7) reproduced the win —
272
- > *no checkpoint over a red check* still **5/5 latched** — and pinned both gaps. Both are
271
+ > ([opus, 2026-07-18](docs/evals/2026-07-18.md), plumbbob 0.8.7) reproduced the win
272
+ > (*no checkpoint over a red check* still **5/5 latched**) and pinned both gaps. Both are
273
273
  > now closed. The side door went by construction: [D67 (auto-not-a-grant)](docs/decisions.md#d67)
274
274
  > (self-approval is human-typed only) retired the settings `auto` grant, so the one
275
- > approval a model could forge is gone. Park capture was never the latch's to reach —
276
- > prose-governed by design ([D10 (pause-not-lock)](docs/decisions.md#d10)/[D13 (no-edit-guards)](docs/decisions.md#d13)) —
275
+ > approval a model could forge is gone. Park capture was never the latch's to reach,
276
+ > prose-governed by design ([D10 (pause-not-lock)](docs/decisions.md#d10)/[D13 (no-edit-guards)](docs/decisions.md#d13)),
277
277
  > and it failed because the guidance couldn't reach a fresh session at all; the turn hook
278
278
  > now injects one line when a step is in flight, and the third sweep
279
279
  > ([opus, 2026-07-27](docs/evals/2026-07-27.md), plumbbob 0.9.0) measures it at **5/5**,
280
280
  > alongside every other contract. That sweep ran the latched arm only, so its numbers are
281
- > a regression check on the shipped configuration, not a fresh baseline delta — the
281
+ > a regression check on the shipped configuration, not a fresh baseline delta: the
282
282
  > 2/5→5/5 headline above remains the 2026-07-11 measurement.
283
283
 
284
284
  ## Documentation
285
285
 
286
- Each doc answers one question — in rough reading order for a new user:
287
-
288
- - *What does a session actually look like?* → [`docs/happy-path.md`](docs/happy-path.md) — one goal walked end to end; read this first.
289
- - *Show me the artifacts it leaves behind.* → [`examples/`](examples/) — that same session's finished build folder, file by file.
290
- - *Should I / can I / what about…?* → [`docs/faq.md`](docs/faq.md) — the adoption questions, answered straight.
291
- - *What is each method for?* → [`docs/techniques.md`](docs/techniques.md) — steps, seams, the pause, park/harvest, spikes.
292
- - *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md) — all thirteen skills: inputs, effects, when to reach for each.
293
- - *How do I plug in my own agent?* → [`docs/agents.md`](docs/agents.md) — the subprocess envelope, the manifest, `harness.json`, and working examples (including a local-model reviewer via Ollama).
294
- - *How do I get a local model reviewing my steps?* → [`docs/local-model-review.md`](docs/local-model-review.md) — the ollama-reviewer example walked end to end, install to every-pause review.
295
- - *How do I install it, exactly?* → [`docs/install.md`](docs/install.md) — the full guide and the agent-neutral roadmap.
296
- - *What does the CLI underneath do?* → [`docs/cli-reference.md`](docs/cli-reference.md) — every verb, flag, exit code, and the `.plumbbob/` sidecar.
297
- - *What are these untracked files, and why is it touching my `.git`?* → [`docs/state-and-git.md`](docs/state-and-git.md) — the state files, the `info/exclude` mechanism, and the objections answered.
298
- - *Something's broken.* → [`docs/troubleshooting.md`](docs/troubleshooting.md) — fixes for the common snags.
299
- - *Why is it built this way?* → [`docs/decisions.md`](docs/decisions.md) — the `D#` / `C#` design-decision key the source cites.
300
- - *How does it hang together inside?* → [`docs/architecture.md`](docs/architecture.md) — the layers and planes, for contributors (in progress).
301
- - *Why does this exist at all?* → [`docs/attention-first-development.md`](docs/attention-first-development.md) — the philosophy: attention as the scarce resource.
302
- - *What does it run on my machine?* → [`SECURITY.md`](SECURITY.md) — execution surface and how to report a vulnerability.
303
- - *How do I contribute?* → [`CONTRIBUTING.md`](CONTRIBUTING.md) — setup, conventions, and how to submit changes.
286
+ Each doc answers one question (in rough reading order for a new user):
287
+
288
+ - *What does a session actually look like?* → [`docs/happy-path.md`](docs/happy-path.md): one goal walked end to end; read this first.
289
+ - *Show me the artifacts it leaves behind.* → [`examples/`](examples/): that same session's finished build folder, file by file.
290
+ - *Should I / can I / what about…?* → [`docs/faq.md`](docs/faq.md): the adoption questions, answered straight.
291
+ - *What is each method for?* → [`docs/techniques.md`](docs/techniques.md): steps, seams, the pause, park/harvest, spikes.
292
+ - *What does each skill do?* → [`docs/skills-reference.md`](docs/skills-reference.md): all thirteen skills: inputs, effects, when to reach for each.
293
+ - *How do I plug in my own agent?* → [`docs/agents.md`](docs/agents.md): the subprocess envelope, the manifest, `harness.json`, and working examples (including a local-model reviewer via Ollama).
294
+ - *How do I get a local model reviewing my steps?* → [`docs/local-model-review.md`](docs/local-model-review.md): the ollama-reviewer example walked end to end, install to every-pause review.
295
+ - *How do I install it, exactly?* → [`docs/install.md`](docs/install.md): the full guide and the agent-neutral roadmap.
296
+ - *What does the CLI underneath do?* → [`docs/cli-reference.md`](docs/cli-reference.md): every verb, flag, exit code, and the `.plumbbob/` sidecar.
297
+ - *What are these untracked files, and why is it touching my `.git`?* → [`docs/state-and-git.md`](docs/state-and-git.md): the state files, the `info/exclude` mechanism, and the objections answered.
298
+ - *Something's broken.* → [`docs/troubleshooting.md`](docs/troubleshooting.md): fixes for the common snags.
299
+ - *Why is it built this way?* → [`docs/decisions.md`](docs/decisions.md): the `D#` / `C#` design-decision key the source cites.
300
+ - *How does it hang together inside?* → [`docs/architecture.md`](docs/architecture.md): the layers and planes, for contributors (in progress).
301
+ - *Why does this exist at all?* → [`docs/attention-first-development.md`](docs/attention-first-development.md): the philosophy: attention as the scarce resource.
302
+ - *Why does AI-written prose get worse over time?* → [`docs/generation-loss.md`](docs/generation-loss.md): the copy-of-a-copy problem, and why anchor texts are written by hand.
303
+ - *What does it run on my machine?* → [`SECURITY.md`](SECURITY.md): execution surface and how to report a vulnerability.
304
+ - *How do I contribute?* → [`CONTRIBUTING.md`](CONTRIBUTING.md): setup, conventions, and how to submit changes.
304
305
 
305
306
  ## License
306
307
 
package/dist/cli-core.js CHANGED
@@ -1,12 +1,12 @@
1
- // plumbbob CLI core — argv dispatch and the help table, separated from the bin
1
+ // plumbbob CLI core: argv dispatch and the help table, separated from the bin
2
2
  // entry (cli.ts) so the routing/help logic is unit-testable in-process without
3
3
  // the top-level process.exit. Node builtins plus a deliberate few dependencies
4
4
  // (currently checkride alone, via check.ts). Functional/procedural: no classes,
5
5
  // no `this`, no default export.
6
6
  //
7
7
  // The deciding/executing boundary is a pause, not a lock, so there is nothing
8
- // to defend in the dispatcher — every verb runs the same whether a human or the
9
- // model triggers it. There is no human-only `mode` escape hatch and no
8
+ // to defend in the dispatcher; every verb runs the same whether a human or the
9
+ // model triggers it. No human-only `mode` escape hatch exists and no
10
10
  // CLAUDECODE in-session refusal; what keeps the human the decider is the pause
11
11
  // at the step boundary (the skills), not a refusal here.
12
12
  import { readFileSync } from 'node:fs';
@@ -38,7 +38,7 @@ const BUILD_FLAG = {
38
38
  *
39
39
  * One table drives three things: the top-level verb table, per-verb `--help`,
40
40
  * and the unknown-flag refusal in `run`. Keeping it as data rather than inline
41
- * writes is the deliberate exception to the write-at-the-call-site convention —
41
+ * writes is the deliberate exception to the write-at-the-call-site convention;
42
42
  * a synopsis has to be readable by the validator, not just printable.
43
43
  */
44
44
  const VERBS = [
@@ -255,7 +255,7 @@ export function verbSpec(name) {
255
255
  return VERBS.find((v) => v.name === name) ?? null;
256
256
  }
257
257
  /**
258
- * Every verb name, in table order — the contract tests iterate this.
258
+ * Every verb name, in table order: the contract tests iterate this.
259
259
  */
260
260
  export function verbNames() {
261
261
  return VERBS.map((v) => v.name);
@@ -363,8 +363,8 @@ async function dispatch(verb, cwd, rest) {
363
363
  * report and exit 1, so the bin entry only ever exits with the code returned
364
364
  * here.
365
365
  *
366
- * `cwd` is the repo the verbs act on. It defaults to the process's — the only
367
- * thing the bin entry ever wants — and is a parameter solely so a test can name
366
+ * `cwd` is the repo the verbs act on. It defaults to the process's (the only
367
+ * thing the bin entry ever wants) and is a parameter solely so a test can name
368
368
  * a fixture. Screening argv does not help when the argv is *valid*: `checkpoint
369
369
  * -m --help` reads `--help` as the subject, exactly as it should, and then
370
370
  * commits. Whose repo it commits is decided here, and a default is not a choice
@@ -374,7 +374,7 @@ export async function run(argv, cwd = process.cwd()) {
374
374
  const verb = argv[0] ?? 'help';
375
375
  const rest = argv.slice(1);
376
376
  if (verb === 'help' || verb === '--help' || verb === '-h') {
377
- // `plumbbob help <verb>` is the same page as `plumbbob <verb> --help` —
377
+ // `plumbbob help <verb>` is the same page as `plumbbob <verb> --help`;
378
378
  // both spellings get typed, so both work.
379
379
  const topic = rest.find((a) => !a.startsWith('-'));
380
380
  if (topic === undefined) {
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // plumbbob CLI entry — the published bin. All routing and the help table live in
2
+ // plumbbob CLI entry: the published bin. All routing and the help table live in
3
3
  // cli-core.ts (unit-tested in-process); this file is only the executable shell:
4
4
  // it wires argv to the dispatcher and turns the returned code into the exit code.
5
5
  // Keeping the single process.exit here is what lets cli-core.ts be imported by a
@@ -1,11 +1,11 @@
1
1
  // The agent contract. A user-authored agent is anything executable that speaks a
2
- // versioned JSON envelope — a JSON manifest on disk (`agent.json`), JSON on the
3
- // child's stdin, JSON on its stdout, prose on its stderr — so the doorway is
2
+ // versioned JSON envelope (a JSON manifest on disk (`agent.json`), JSON on the
3
+ // child's stdin, JSON on its stdout, prose on its stderr) so the doorway is
4
4
  // runtime-agnostic: any language, any framework, one subprocess boundary. This
5
5
  // module is the contract's validator, resolver, and runner: it type-narrows the
6
6
  // on-disk manifest and the output envelope a child returns (refusing a contract
7
7
  // major-version mismatch with an upgrade hint rather than guessing at a shape
8
- // that is only stable within a major — additions are a minor bump, removals or
8
+ // that is only stable within a major: additions are a minor bump, removals or
9
9
  // renames a major), it walks the agent tiers to resolve an agent by name, it
10
10
  // composes the step context a child reads on stdin, and it spawns the child.
11
11
  // The validators are pure; the resolver reads `agent.json` files off disk with
@@ -28,7 +28,7 @@ export const CONTRACT_VERSION = 1;
28
28
  * The three lifecycle slots an agent may bind to: `before` (context in),
29
29
  * `build` (the diff), `after` (advisory review).
30
30
  *
31
- * Exactly these, never a fourth — "a salient point in the middle" is judgment
31
+ * Exactly these, never a fourth; "a salient point in the middle" is judgment
32
32
  * the host model handles in prose, not a declarative slot.
33
33
  */
34
34
  export const SLOTS = ['before', 'build', 'after'];
@@ -122,7 +122,7 @@ export function parseEnvelope(raw) {
122
122
  * True when a string names one of the three slots.
123
123
  *
124
124
  * Exported for the `agent run` verb's `--mode` refusal: a mode the manifest
125
- * doesn't declare is refused loud — the user who typed it asked for that slot
125
+ * doesn't declare is refused loud; the user who typed it asked for that slot
126
126
  * specifically.
127
127
  */
128
128
  export function isSlot(value) {
@@ -169,7 +169,7 @@ function parseSlots(value) {
169
169
  /**
170
170
  * Narrow the envelope's optional `parked[]` (absent = none).
171
171
  *
172
- * Present, it must be an array of non-blank strings — each becomes a park line
172
+ * Present, it must be an array of non-blank strings: each becomes a park line
173
173
  * the CLI lands through the park verb. A malformed list is refused rather than
174
174
  * silently dropped: losing a parked concern is a quiet data loss. Returns the
175
175
  * trimmed lines or an error string.
@@ -219,13 +219,13 @@ function envelopeFail(error) {
219
219
  return { ok: false, error };
220
220
  }
221
221
  /**
222
- * The project agents tier — `.plumbbob/agents/` under the repo root.
222
+ * The project agents tier: `.plumbbob/agents/` under the repo root.
223
223
  */
224
224
  export function projectAgentsDir(root) {
225
225
  return join(root, '.plumbbob', 'agents');
226
226
  }
227
227
  /**
228
- * The personal agents tier — `~/.plumbbob/agents/`, the user's own library.
228
+ * The personal agents tier: `~/.plumbbob/agents/`, the user's own library.
229
229
  */
230
230
  export function personalAgentsDir(home) {
231
231
  return join(home, '.plumbbob', 'agents');
@@ -246,7 +246,7 @@ function agentsHome(home) {
246
246
  * A `flagPath` (from `--agent <path>`) points straight at an agent directory
247
247
  * and takes top priority. A tier "hits" when it holds an `agent.json`: a
248
248
  * malformed one there is surfaced as an error (project still shadows
249
- * personal), never silently skipped in favor of a different agent — an
249
+ * personal), never silently skipped in favor of a different agent; an
250
250
  * explicit ask fails loud. Returns the not-found error only when no tier holds
251
251
  * a manifest at all.
252
252
  */
@@ -273,7 +273,7 @@ export function resolveAgent(root, name, opts = {}) {
273
273
  /**
274
274
  * Every resolvable agent across both tiers, sorted by name.
275
275
  *
276
- * Project shadows personal: a name defined in both resolves — and lists — from
276
+ * Project shadows personal: a name defined in both resolves (and lists) from
277
277
  * project. Each entry carries its origin and resolution; a malformed manifest
278
278
  * lands as an errored resolution rather than crashing the walk.
279
279
  */
@@ -325,7 +325,7 @@ export function formatAgentList(listings) {
325
325
  * Returns null when the directory holds no manifest (a tier miss, so
326
326
  * resolution falls through), an errored resolution when the manifest is
327
327
  * present but unreadable/malformed, else the resolved agent. A missing file
328
- * (ENOENT throw) is the miss signal — one read, no TOCTOU race.
328
+ * (ENOENT throw) is the miss signal: one read, no TOCTOU race.
329
329
  */
330
330
  function loadAgentDir(dir, origin) {
331
331
  const file = join(dir, 'agent.json');
@@ -367,10 +367,10 @@ function agentDirNames(dir) {
367
367
  /**
368
368
  * Compose the StepContext for one step.
369
369
  *
370
- * The strict seam parse is the only refusal path — a missing step or
370
+ * The strict seam parse is the only refusal path: a missing step or
371
371
  * unparseable seam gates git behavior, so it fails loud; everything else is
372
372
  * best-effort and lands whatever it can, returning `warnings` (skipped-bullet
373
- * lines) for the caller to print on stderr rather than writing there itself —
373
+ * lines) for the caller to print on stderr rather than writing there itself;
374
374
  * this module stays pure, the verb does the IO.
375
375
  */
376
376
  export function composeStepContext(params) {
@@ -410,7 +410,7 @@ function skipWarnings(heading, skipped) {
410
410
  * Validate a parsed harness.json.
411
411
  *
412
412
  * Contract first (a major mismatch gets the same upgrade hint as the manifest
413
- * and envelope). Structure is strict — a `steps` that is not an object, a
413
+ * and envelope). Structure is strict: a `steps` that is not an object, a
414
414
  * non-numeric step key, or a step entry that is not an object is the author's
415
415
  * error and is refused loud. Slot *contents* stay lenient: a slot value may be
416
416
  * one name or a list, and blanks/non-strings drop rather than refuse, because
@@ -445,9 +445,9 @@ function parseHarness(raw) {
445
445
  /**
446
446
  * Read and validate a harness.json at `path`.
447
447
  *
448
- * The caller builds the path from the build folder — this module stays
448
+ * The caller builds the path from the build folder; this module stays
449
449
  * ignorant of the sidecar layout, taking the full path. Returns null when the
450
- * file is absent (a build with no bound agents — a clean no-op); an errored
450
+ * file is absent (a build with no bound agents: a clean no-op); an errored
451
451
  * parse when present-but-broken (invalid JSON or a malformed structure the
452
452
  * author must fix); else the validated bindings. Shared so every reader
453
453
  * (`agent run`, `status`) parses the harness the same way.
@@ -474,7 +474,7 @@ export function readHarnessFile(path) {
474
474
  *
475
475
  * Accepts a harness `defaults` block, a per-step entry, or the settings-level
476
476
  * defaults. A slot value may be a single agent name or a list; blanks and
477
- * non-strings drop. Only the three real slots are read — `note` and any
477
+ * non-strings drop. Only the three real slots are read; `note` and any
478
478
  * stranger key are ignored here. A slot present but naming no valid agent
479
479
  * still counts as bound-to-nothing (an explicit override to none), so callers
480
480
  * get clean replace semantics up the ladder.
@@ -493,7 +493,7 @@ export function parseSlotBindings(raw) {
493
493
  * The agents bound to one slot for one step, merged down the bindings ladder.
494
494
  *
495
495
  * A per-step slot entry overrides the harness `defaults`, which override the
496
- * settings-level defaults — the first level that names the slot wins, replace
496
+ * settings-level defaults: the first level that names the slot wins, replace
497
497
  * not append, the same most-specific-wins shape as the settings ladder. The
498
498
  * `--agent` flag and an explicit name sit above all of this, but the verb
499
499
  * takes the single-agent path for those and never reaches here. Returns []
@@ -511,7 +511,7 @@ export function resolveSlotAgents(params) {
511
511
  return [];
512
512
  }
513
513
  /**
514
- * Coerce a slot value — one agent name or a list — to a trimmed name list,
514
+ * Coerce a slot value (one agent name or a list) to a trimmed name list,
515
515
  * dropping blanks and non-strings.
516
516
  */
517
517
  function asNameList(value) {
@@ -519,7 +519,7 @@ function asNameList(value) {
519
519
  return items.filter((v) => typeof v === 'string' && v.trim().length > 0).map((v) => v.trim());
520
520
  }
521
521
  /**
522
- * True when a value is a plain JSON object — not null, not an array.
522
+ * True when a value is a plain JSON object: not null, not an array.
523
523
  */
524
524
  function isObject(value) {
525
525
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -532,13 +532,13 @@ function isObject(value) {
532
532
  * directory rides in `PLUMBBOB_AGENT_DIR` so a root-cwd agent can still reach
533
533
  * its files. The composed StepContext is delivered as JSON on stdin. Stdout is
534
534
  * piped and captured (the envelope); stderr is inherited so the child's prose
535
- * streams live to the terminal — production (narrating) never collides with
535
+ * streams live to the terminal: production (narrating) never collides with
536
536
  * consumption (one structured result at the pause). A SIGINT while the child
537
- * runs kills it and reports `interrupted` rather than orphaning it — the human
537
+ * runs kills it and reports `interrupted` rather than orphaning it; the human
538
538
  * is present, Ctrl-C works. A positive `timeoutSeconds` arms a kill timer
539
539
  * (0 = off; a timeout is the user's explicit opt-in). Async `spawn`, not
540
540
  * `spawnSync`, so the parent stays live to interrupt gracefully. Never
541
- * rejects — every failure mode is a resolved `AgentRunResult`.
541
+ * rejects: every failure mode is a resolved `AgentRunResult`.
542
542
  */
543
543
  export function runAgent(params) {
544
544
  return new Promise((resolve) => {
@@ -576,7 +576,7 @@ export function runAgent(params) {
576
576
  child.stdout.on('data', (chunk) => {
577
577
  stdout += chunk.toString();
578
578
  });
579
- // A shell that never launched (e.g. `sh` itself missing) — distinct from a
579
+ // A shell that never launched (for example `sh` itself missing): distinct from a
580
580
  // command that ran and exited non-zero, which arrives on `close`.
581
581
  child.on('error', (err) => {
582
582
  finish({ ok: false, reason: 'spawn', error: err.message });
@@ -599,9 +599,9 @@ export function runAgent(params) {
599
599
  /**
600
600
  * Parse the child's captured stdout into a validated envelope.
601
601
  *
602
- * JSON first (a non-JSON stdout is out of contract — the envelope must be one
602
+ * JSON first (a non-JSON stdout is out of contract: the envelope must be one
603
603
  * JSON object), then the envelope validator, which carries the
604
- * contract-mismatch hint. Kept private — `runAgent` is the only caller and the
604
+ * contract-mismatch hint. Kept private; `runAgent` is the only caller and the
605
605
  * verb reads the union, not this.
606
606
  */
607
607
  function parseChildEnvelope(stdout) {