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
@@ -0,0 +1,41 @@
1
+ # The lexical fingerprints of model-generated prose: vocabulary that runs at
2
+ # many times its human base rate in machine-written text (`delve` is the
3
+ # canonical case), and the stock frames readers have learned to skim past.
4
+ # Model output feeds the next session's context, so every fingerprint that
5
+ # lands becomes the example the next generation copies — this rule breaks that
6
+ # loop while the copy count is still one.
7
+ #
8
+ # The list is deliberately short and high-precision: words a careful writer
9
+ # almost never reaches for, so a hit is close to proof of provenance. Words
10
+ # that are merely overused (`robust`, `crucial`, `comprehensive`) stay off it —
11
+ # flagging those turns the rule into noise. Add your own repo's tells as they
12
+ # emerge; cut anything you would defend in your own voice.
13
+ #
14
+ # Case classes instead of `ignorecase`, because the last token — the
15
+ # `It's not just X — it's Y` frame — is anchored to its sentence-initial
16
+ # capital, and one flag governs every token in the rule.
17
+ extends: existence
18
+ message: "'%s' is a fingerprint of generated prose — say the plain thing instead."
19
+ level: error
20
+ ignorecase: false
21
+ # The tokens are full regexes with their own anchors, so word boundaries must
22
+ # not be wrapped around them.
23
+ nonword: true
24
+ tokens:
25
+ - '\b[Dd]elv(?:e[sd]?|ing)\b'
26
+ - '\b[Cc]ommendable\b'
27
+ - '\b[Mm]eticulous(?:ly)?\b'
28
+ - '\b[Ii]ntricate(?:ly)?\b'
29
+ - '\b[Ii]ntricac(?:y|ies)\b'
30
+ - '\b[Tt]estament to\b'
31
+ - '\b[Tt]apestry\b'
32
+ - '\b[Gg]ame-chang(?:er|ers|ing)\b'
33
+ - '\b[Ee]ver-evolving\b'
34
+ - "\\b[Ii]n today['’]s\\b"
35
+ - '\b[Ss]eamless(?:ly)?\b'
36
+ - '\b[Ss]upercharg(?:e[sd]?|ing)\b'
37
+ - '\b[Uu]nlock(?:s|ed|ing)? the (?:full )?(?:power|potential)\b'
38
+ - "\\b[Ii]t(?: is|['’]s) (?:important to note|worth noting)\\b"
39
+ - '\b[Ii]n conclusion\b'
40
+ - '\b[Bb]oasts\b'
41
+ - "It(?:['’]s| is) not just (?:about )?[^.,;:—–]{1,60}[—–,;] ?it(?:['’]s| is)\\b"
@@ -0,0 +1,14 @@
1
+ # Latin abbreviations read as shorthand and land as a stumble: `i.e.` and `e.g.`
2
+ # are confused for each other often enough that the plain English is simply
3
+ # clearer. Mechanical, not a matter of voice — the substitution is always
4
+ # available and always says the same thing.
5
+ extends: substitution
6
+ message: "Use '%s' instead of '%s'."
7
+ level: error
8
+ ignorecase: false
9
+ # The tokens carry periods, so word boundaries cannot wrap them.
10
+ nonword: true
11
+ swap:
12
+ '\b(?:eg|e\.g\.)[\s,]': for example
13
+ '\b(?:ie|i\.e\.)[\s,]': that is
14
+ '\betc\.': and so on
@@ -0,0 +1,28 @@
1
+ # An adverb ending in `-ly` cannot modify anything but the word that follows
2
+ # it, so the hyphen adds nothing: `newly created`, not `newly-created`. This is
3
+ # a rule about punctuation, not about how the sentence should read.
4
+ #
5
+ # The exceptions are words that end in `-ly` without being adverbs — `supply
6
+ # chain`, `family friendly`, `early return`. They are the rule's whole
7
+ # false-positive surface, and the list below is a starting point: add to it
8
+ # rather than reaching for a suppression.
9
+ extends: existence
10
+ message: "'%s': an adverb ending in '-ly' takes no hyphen."
11
+ level: error
12
+ ignorecase: true
13
+ # The token spans a hyphen, which word boundaries would cut in half.
14
+ nonword: true
15
+ tokens:
16
+ - '\b[^\s-]+ly-\w+\b'
17
+ exceptions:
18
+ - anomaly
19
+ - apply
20
+ - assembly
21
+ - comply
22
+ - early
23
+ - family
24
+ - imply
25
+ - multiply
26
+ - only
27
+ - reply
28
+ - supply
@@ -0,0 +1,33 @@
1
+ # The minted dialect: verbs pressed into service as nouns (`the ask`), nouns
2
+ # conscripted as verbs (`to action`), and the corporate register's syllable
3
+ # inflations (`utilize`, three syllables where one does the work). Generated
4
+ # prose leans into this dialect and then copies its own lean, so each swap here
5
+ # is one the next pass never gets to imitate. Mechanical, not a matter of
6
+ # voice — the plain word is always available and always says the same thing.
7
+ #
8
+ # Only unambiguous forms are listed. The noun `leverage` (mechanical advantage,
9
+ # `open-source leverage`) is legitimate English, so only the verb forms match;
10
+ # `the build` stays off the list entirely because in a repository it names the
11
+ # compiled artifact far more often than it apes the dialect.
12
+ extends: substitution
13
+ message: "Use '%s' instead of '%s'."
14
+ level: error
15
+ ignorecase: false
16
+ # The patterns carry their own case classes and boundaries.
17
+ nonword: true
18
+ swap:
19
+ '\b[Tt]he ask\b': the request
20
+ '\b[Ll]earnings\b': lessons
21
+ '\b[Aa]ctioned\b': handled
22
+ '\b[Aa]ctioning\b': handling
23
+ '\b[Ss]olutioning\b': designing
24
+ '\b[Tt]o leverage\b': to use
25
+ '\b[Ll]everages\b': uses
26
+ '\b[Ll]everaged\b': used
27
+ '\b[Ll]everaging\b': using
28
+ '\b[Uu]tilize\b': use
29
+ '\b[Uu]tilizes\b': uses
30
+ '\b[Uu]tilized\b': used
31
+ '\b[Uu]tilizing\b': using
32
+ '\b[Uu]tilization\b': use
33
+ '\b[Tt]he lift\b': the effort
@@ -0,0 +1,16 @@
1
+ # `There is a flag that controls X` spends its subject position on a word that
2
+ # names nothing, then makes the reader wait for the real subject. Naming the
3
+ # thing first is shorter every time: `A flag controls X`.
4
+ #
5
+ # Deliberately case-sensitive: only the sentence-initial pattern is the
6
+ # stumble. A mid-clause existential — `when there is nothing to fix` — is
7
+ # idiomatic English with no cleaner rewrite, and flagging it turns careful
8
+ # prose into a wall of contortions. Sentence-initial `There` is always
9
+ # capitalized, so the capital T is the whole test.
10
+ extends: existence
11
+ message: "'%s' postpones the subject — name the thing that acts, then act."
12
+ level: error
13
+ ignorecase: false
14
+ tokens:
15
+ - 'There (is|are|was|were)'
16
+ - "There'?s"
@@ -0,0 +1,26 @@
1
+ # Weasel words claim an intensity the sentence never earns: `very fast` is not
2
+ # a measurement, and `simply call the function` is a claim about the reader.
3
+ # Cutting one almost never costs meaning.
4
+ #
5
+ # This is the one rule in the set that is taste rather than mechanics, so the
6
+ # scaffolded .vale.ini ships it OFF (`Repo.Weasel = NO`). Careful prose uses
7
+ # some of these deliberately — `just` as "only", `clearly` as a genuine signal —
8
+ # and a rule the author disagrees with becomes a wall of suppressions. Read the
9
+ # list, cut what you would defend, then turn it on.
10
+ extends: existence
11
+ message: "'%s' is a weasel word — cut it, or say the specific thing."
12
+ level: error
13
+ ignorecase: true
14
+ tokens:
15
+ - actually
16
+ - basically
17
+ - clearly
18
+ - fairly
19
+ - just
20
+ - obviously
21
+ - quite
22
+ - rather
23
+ - really
24
+ - simply
25
+ - somewhat
26
+ - very
@@ -0,0 +1,34 @@
1
+ # Vale — the `prose` slot. Written by `checkride init --add prose`; yours now.
2
+ #
3
+ # The styles under .vale/styles/Repo are plain YAML in this repo: no download,
4
+ # no `vale sync` on the default path, nothing to keep in step with an upstream.
5
+ # Disagree with a rule and the answer is to edit or delete it, not to suppress
6
+ # it line by line — a style full of suppressions is ceremony.
7
+
8
+ StylesPath = .vale/styles
9
+
10
+ # Report everything; only errors decide the check. Vale exits non-zero for
11
+ # error-severity alerts alone, so demoting a rule to `warning` keeps it visible
12
+ # here while making it advisory — which is why this stays at `suggestion`.
13
+ # Raise it and you stop seeing the advisory half rather than silencing it.
14
+ MinAlertLevel = suggestion
15
+
16
+ # Vale has no TypeScript format of its own. Mapping it to JavaScript lints doc
17
+ # comments and string-free prose, and leaves the code alone.
18
+ [formats]
19
+ ts = js
20
+
21
+ [*.{md,ts}]
22
+ BasedOnStyles = Vale, Repo
23
+
24
+ # One spell checker, one wordlist. The `spell` slot (cspell) owns spelling and
25
+ # knows the technical vocabulary a plain en_US dictionary flags as errors.
26
+ Vale.Spelling = NO
27
+
28
+ # A vocabulary file is that second wordlist through another door — same reason.
29
+ Vale.Terms = NO
30
+
31
+ # The subjective half. Every other rule here is mechanical; this one is taste,
32
+ # and taste belongs to whoever writes the docs. Read
33
+ # .vale/styles/Repo/Weasel.yml and flip this to YES if you agree with it.
34
+ Repo.Weasel = NO
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plumbbob",
3
- "version": "0.9.5",
3
+ "version": "0.10.0",
4
4
  "description": "Attention-first build process: 12 skills + a CLI that keep you the decider — guidance on the work, a latch on the record.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -45,21 +45,22 @@
45
45
  "eval:report": "node test/evals/run.ts --report"
46
46
  },
47
47
  "devDependencies": {
48
- "@ast-grep/cli": "0.43.0",
48
+ "@ast-grep/cli": "0.45.1",
49
49
  "@stryker-mutator/api": "9.6.1",
50
50
  "@stryker-mutator/core": "9.6.1",
51
51
  "@stryker-mutator/vitest-runner": "9.6.1",
52
- "@types/node": "24.13.1",
53
- "@vitest/coverage-v8": "4.1.9",
54
- "fallow": "2.103.0",
52
+ "@types/node": "26.2.0",
53
+ "@vitest/coverage-v8": "4.1.10",
54
+ "@vvago/vale": "3.17.1",
55
+ "fallow": "3.14.0",
55
56
  "fascicle": "0.8.16",
56
- "markdownlint-cli2": "0.22.1",
57
- "oxlint": "1.69.0",
57
+ "markdownlint-cli2": "0.23.2",
58
+ "oxlint": "1.78.0",
58
59
  "typescript": "6.0.3",
59
- "vitest": "4.1.9"
60
+ "vitest": "4.1.10"
60
61
  },
61
62
  "dependencies": {
62
- "checkride": "0.10.2"
63
+ "checkride": "0.12.2"
63
64
  },
64
65
  "bundleDependencies": [
65
66
  "checkride"
@@ -1,116 +1,121 @@
1
1
  ---
2
2
  name: build
3
- description: The default engine — read the next planned step from intent, implement it (its done-when, seam, Decisions, Constraints), then verify it through to the approval pause. Swappable — build by hand/vibed/another harness and run /plumbbob:verify instead. `--auto` self-approves and chains to done; a step range like `1-3` self-approves through step 3, then pauses.
3
+ description: "The default engine: read the next planned step from intent, implement it (its done-when, seam, Decisions, Constraints), then verify it through to the approval pause. Swappable: build by hand/vibed/another harness and run /plumbbob:verify instead. `--auto` self-approves and chains to done; a step range like `1-3` self-approves through step 3, then pauses."
4
4
  argument-hint: "[step-number | step-range] [--auto]"
5
5
  disable-model-invocation: true
6
6
  allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob build:*), Bash(plumbbob handoff:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(plumbbob park:*), Bash(plumbbob agent:*), Bash(plumbbob spike:*), Bash(git diff:*)
7
7
  ---
8
8
 
9
- # PlumbBob — build a step (the default engine)
9
+ # PlumbBob: build a step (the default engine)
10
10
 
11
11
  Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not on PATH in this session. Marketplace install: confirm the plugin is enabled in /plugin, then /reload-plugins. Skills-dir/global install: npm i -g plumbbob && plumbbob init."`
12
12
 
13
- This is the **bundled executor** — the default engine, not the only one. It is
13
+ This is the **bundled executor**: the default engine, not the only one. It is
14
14
  **swappable**: you can implement any step by hand, in a vibe session, or with
15
- another harness and go straight to `/plumbbob:verify` instead — plumbbob does not care how
15
+ another harness and go straight to `/plumbbob:verify` instead; plumbbob does not care how
16
16
  the diff appeared. When you do run it, it reads the plan, writes the step, and
17
17
  carries straight through to the verify pause.
18
18
 
19
19
  Since `/plumbbob:plan` lays down the whole step list up front, the happy path is to run
20
- `/plumbbob:build` once per step until done — each run builds the next undone step and stops
20
+ `/plumbbob:build` once per step until done: each run builds the next undone step and stops
21
21
  at the pause for your approval. **Running `/plumbbob:build` again is itself the clock tick.**
22
22
 
23
- A model note: this skill **inherits the session model** — nothing pins or switches
23
+ A model note: this skill **inherits the session model**; nothing pins or switches
24
24
  it. If the step you're about to build carries a `- model:` recommendation that
25
- differs from the model you're running as, say so before implementing — the human can
25
+ differs from the model you're running as, say so before implementing; the human can
26
26
  switch with `/model` and rerun to honor it, or wave you on. Advisory, never a gate.
27
27
 
28
28
  ## What this skill does, in order
29
29
 
30
- 1. **Pick the step.** Use the number you were invoked with (e.g. `/plumbbob:build 4`) — or,
30
+ 1. **Pick the step.** Use the number you were invoked with (for example `/plumbbob:build 4`), or,
31
31
  if you were given a range like `/plumbbob:build 1-3`, start at the first number and treat
32
32
  the second as the auto-approve ceiling (see the range note under `--auto`). With no
33
- argument, don't resolve the next step yourself — the CLI does: bare `plumbbob build`
33
+ argument, don't resolve the next step yourself; the CLI does: bare `plumbbob build`
34
34
  (step 2) enters the next undone step and refuses with a `/plumbbob:step` nudge when every
35
35
  step is checkpointed.
36
- 2. **Enter the step.** Run `plumbbob build <n>` — or bare `plumbbob build` to enter the
36
+ 2. **Enter the step.** Run `plumbbob build <n>`, or bare `plumbbob build` to enter the
37
37
  next undone step (records the in-flight STEP + SEAM so `/plumbbob:status` shows the step in
38
38
  flight; the seam is awareness, not a lock).
39
39
  - **Spike-as-step.** If the step's title opens with `spike:` / `Spike:`, the increment
40
- *is* the experiment — resolving a fork the plan couldn't settle on paper. After
40
+ *is* the experiment: resolving a fork the plan couldn't settle on paper. After
41
41
  entering it, run `plumbbob spike report "<slug>"` (the CLI scaffolds a
42
42
  `spike-NN-<slug>.md` in the build folder, stamped `via: step <n>`), then work the
43
43
  experiment and record your **Findings** and **Verdict** there. A recorded verdict is
44
- what the step's done-when should check — the report, not just the code, is the
44
+ what the step's done-when should check; the report, not just the code, is the
45
45
  deliverable. This is the same artifact `/plumbbob:spike` produces for a mid-build fork
46
- — D70 (spike-reports).
46
+ ([D70 (spike-reports)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d70)).
47
47
  3. **Read the plan.** Read the step's **done-when**, its **seam**, and the
48
- **Decisions** and **Constraints** in `intent.md`. Build to *that* — the deciding
48
+ **Decisions** and **Constraints** in `intent.md`. Build to *that*; the deciding
49
49
  already happened, off the chat.
50
50
  - **Bound `before`-agents load context first.** If `plumbbob status` shows a
51
- `harness bindings:` block, this build binds agents — see **§ Running bound agents**
51
+ `harness bindings:` block, this build binds agents; see **§ Running bound agents**
52
52
  and load their context in before you write. No bindings, no harness: just build.
53
53
  4. **Implement** the step, and only that step, staying within the declared seam. A
54
54
  new problem or "ooh what if" that surfaces mid-build is a park, **not** an edit:
55
- capture it by running `plumbbob park "<one tidy line>"` — **saying "let's defer
55
+ capture it by running `plumbbob park "<one tidy line>"`: **saying "let's defer
56
56
  that" writes nothing; only the park line in the build log is a capture.** When the
57
57
  *human* hands you the tangent mid-build, their message **is** the approval to park
58
- it — capture it right away, say what you parked, and stay on the step. If you
58
+ it; capture it right away, say what you parked, and stay on the step. If you
59
59
  genuinely cannot finish without touching more than the seam, that is scope drift:
60
60
  surface it to the human rather than sprawling.
61
61
  - **Bound agents can shape the build.** If `plumbbob status` shows a
62
62
  `harness bindings:` block, a `build`-slot agent may author the diff, a `when`-cue may
63
- fire an agent mid-build, and a non-`done` envelope routes by its status — see
63
+ fire an agent mid-build, and a non-`done` envelope routes by its status; see
64
64
  **§ Running bound agents**.
65
65
  5. **Verify, through to the pause.** Run the verify tick exactly as `/plumbbob:verify`
66
66
  does: `plumbbob check` (on red, read `.check/summary.json` and the failing slot's
67
67
  raw output under `.check/` for the actual diagnostics; while iterating on a fix,
68
- narrow the loop with `plumbbob check --bail --only <slots>` — the checkpoint gate
68
+ narrow the loop with `plumbbob check --bail --only <slots>`; the checkpoint gate
69
69
  still runs everything) → self-review the diff against the done-when, the Decisions,
70
70
  and the Constraints (a single structured read; cite each with its slug from
71
- `intent.md` — `C1 (no-new-deps)`, never a bare `C1`) → validate → **PAUSE
71
+ `intent.md`: `C1 (no-new-deps)`, never a bare `C1`) → validate → **PAUSE
72
72
  for the human's approval** → only on approval, checkpoint with
73
- `plumbbob checkpoint <n> --body <<'BODY' … BODY` — a commit body **proportional to the
73
+ `plumbbob checkpoint <n> --body <<'BODY' … BODY`: a commit body **proportional to the
74
74
  step** (a line for a trivial change, a short paragraph for a meatier one; no TIL scan,
75
75
  no separate commit skill). The CLI owns the subject and appends this step to the
76
- build-log's `## Log`, so the history writes itself — you only supply the body (or omit
76
+ build-log's `## Log`, so the history writes itself; you only supply the body (or omit
77
77
  `--body` for the deterministic done-when + seam + diffstat fallback). Do **not** bump
78
- the version or changelog — that is the human's `/version` call.
78
+ the version or changelog; that is the human's `/version` call.
79
+ - **`--body` reads stdin only.** Redirect the heredoc *into* the command (`--body
80
+ <<'BODY'`). Do **not** pass it as an argument value (`--body "$(cat <<'BODY'…)"`):
81
+ `--body` ignores an argument and always reads stdin. Under an agent harness that
82
+ stdin is a socket, and `--body` now refuses rather than blocking on one; the
83
+ refusal names this exact heredoc form.
79
84
  - **A drifted subject is reconciled at the pause, never silently.** The planned
80
- title *is* the checkpoint subject — D68 (conventional-subjects). If the diff drifted
81
- from it, **present** a corrected subject at the pause — `planned title → proposed
82
- subject`, one line — for explicit approval, and only on approval land it by adding
85
+ title *is* the checkpoint subject ([D68 (conventional-subjects)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d68)). If the diff drifted
86
+ from it, **present** a corrected subject at the pause (`planned title → proposed
87
+ subject`, one line) for explicit approval, and only on approval land it by adding
83
88
  `-m "<subject>"` to the checkpoint. Present nothing and the deterministic
84
89
  title-derived subject lands untouched: the reconcile is the human-approved exception,
85
- not a quiet `-m` swap (which is the agent-authored subject D68 (conventional-subjects)
90
+ not a quiet `-m` swap (which is the agent-authored subject [D68 (conventional-subjects)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d68)
86
91
  refuses).
87
92
  - **If `plumbbob status` shows a `harness bindings:` block**, fold any bound
88
- `after`-agents into that self-review as advisory input — see **§ Running bound
93
+ `after`-agents into that self-review as advisory input; see **§ Running bound
89
94
  agents**.
90
95
 
91
96
  **The latch makes this pause real.** On the default (non-`--auto`) path you
92
- build the step and reach the pause *in one turn* — so under plumbbob's turn hook the
97
+ build the step and reach the pause *in one turn*, so under plumbbob's turn hook the
93
98
  checkpoint at the end of that turn is **refused** ("no human turn since this step
94
99
  began"), and that refusal **is** the pause working as designed. Do exactly what the
95
100
  pause asks: present the diff and the self-review, emit the closing block below, and
96
- **end the turn**. The human's next message — their approval — is the human turn that
101
+ **end the turn**. The human's next message (their approval) is the human turn that
97
102
  lets the checkpoint land: on that turn you run `plumbbob checkpoint <n>` (now allowed)
98
- and **stop at the boundary**. Landing the checkpoint is its own deliberate beat — the
99
- thing approval triggers — never a side effect of starting the next step, and
103
+ and **stop at the boundary**. Landing the checkpoint is its own deliberate beat (the
104
+ thing approval triggers), never a side effect of starting the next step, and
100
105
  `/plumbbob:build` only ever starts the *next* step. **Never route around a refusal with a
101
- raw `git commit`** — the work plane stays free, but the *record* is latched on purpose.
106
+ raw `git commit`**; the work plane stays free, but the *record* is latched on purpose.
102
107
 
103
108
  **End every default build turn with the standardized hand-off block.** Run `plumbbob
104
- handoff` and present its output — it renders the same three-part shape (the state, the
109
+ handoff` and present its output; it renders the same three-part shape (the state, the
105
110
  choice, what's next) straight from the session: step N built, the looks-good /
106
111
  needs-work choice, and the next undone step with its `- model:` recommendation. Show
107
112
  your diff and self-review above it. The CLI owns the block so it can't drift from what
108
113
  `/plumbbob:status` reports; you supply only the judgment above it. It already drops the model
109
- clause when the next step has none, and says so when no planned step remains — you
114
+ clause when the next step has none, and says so when no planned step remains; you
110
115
  don't reproduce the template by hand.
111
116
 
112
117
  **Then hand off with the next model.** Once the checkpoint lands in the approval turn,
113
- run `plumbbob handoff` again and relay its boundary block — with the step gone from
118
+ run `plumbbob handoff` again and relay its boundary block; with the step gone from
114
119
  in-flight it renders "step N checkpointed" and points at the next undone step, carrying
115
120
  that step's `- model:` recommendation (the plan's smallest-model-that-fits call) so the
116
121
  human knows which `/model` to select before running `/plumbbob:build` again. It is what
@@ -121,19 +126,19 @@ switch with `/model` and rerun to honor it, or wave you on. Advisory, never a ga
121
126
  ## Running bound agents
122
127
 
123
128
  Skip this section unless `plumbbob status` shows a `harness bindings:` block. Nearly every
124
- build binds **no** agents — for them the default path above is the whole story. When a
129
+ build binds **no** agents; for them the default path above is the whole story. When a
125
130
  harness *is* bound, this is what each affected step gains, in lifecycle order.
126
131
 
127
- - **`before` — load context in (step 3).** If the build's `harness.json` binds agents to
132
+ - **`before`: load context in (step 3).** If the build's `harness.json` binds agents to
128
133
  this step's `before` slot, run `plumbbob agent run --step <n> --mode before` first: each
129
134
  returns a validated envelope on stdout that plumbbob also appends to the step's
130
135
  `handoff.json`, and its `summary`/`body` become **context you read into the build** (that
131
- is the whole point of the slot — load the context in before you write). No binding, or no
132
- harness, is a clean no-op — just build.
133
- - **`build` — delegate the diff (step 4).** If a `build`-slot agent is bound, run
136
+ is the whole point of the slot: load the context in before you write). No binding, or no
137
+ harness, is a clean no-op: just build.
138
+ - **`build`: delegate the diff (step 4).** If a `build`-slot agent is bound, run
134
139
  `plumbbob agent run --step <n> --mode build` and let that agent author the step's code
135
140
  instead of writing it yourself; its envelope reports what it did. You still own the verify
136
- tick — the diff is reviewed the same way whoever wrote it.
141
+ tick; the diff is reviewed the same way whoever wrote it.
137
142
  - **A manifest's `when` prose is your cue to run an agent mid-build (step 4).** The three
138
143
  slots are the only *declarative* lifecycle points; there is no config for "a salient
139
144
  moment in the middle." That is judgment, and you are the frontier model in the room: when
@@ -142,17 +147,17 @@ harness *is* bound, this is what each affected step gains, in lifecycle order.
142
147
  are the workflow engine.
143
148
  - **Route a non-`done` envelope by its status (step 4).** An agent that returns `blocked`
144
149
  couldn't finish (missing input, failed precondition): surface its `notes`, let the human
145
- unblock, and re-run it — don't work around it. One that returns `drift` finished but found
150
+ unblock, and re-run it; don't work around it. One that returns `drift` finished but found
146
151
  the plan no longer matches reality: **stop and send the human to `/plumbbob:refine`** to repair
147
152
  the plan before continuing, rather than building on a plan that's now wrong. A non-zero
148
153
  exit is a failed run: report it and stop.
149
- - **`after` — advisory input to the verify tick (step 5).** After `plumbbob check`, run any
154
+ - **`after`: advisory input to the verify tick (step 5).** After `plumbbob check`, run any
150
155
  bound `after`-agents (`plumbbob agent run --step <n> --mode after`) and fold their
151
- envelopes into the self-review as **advisory input** — they inform, they never gate
156
+ envelopes into the self-review as **advisory input**; they inform, they never gate
152
157
  (checkride gates, the human is the clock; an `after`-agent that could fail a step would
153
158
  be the old lock back in another form).
154
159
 
155
- ## `--auto` — let the agent be the clock (opt-in)
160
+ ## `--auto`: let the agent be the clock (opt-in)
156
161
 
157
162
  `/plumbbob:build --auto` is the explicit escape hatch when the human wants unattended
158
163
  progress instead of approving each step. It does the same work, but **the agent reviews
@@ -162,41 +167,41 @@ and approves in the human's place**, and it **chains**:
162
167
  order as the default path: bound `before`-agents → implement (or the bound `build`-agent) → bound
163
168
  `after`-agents → `check` → self-review → **if the check is green AND the
164
169
  self-review finds no done-when / Decision / Constraint mismatch, checkpoint** and move
165
- straight on to the next planned step. Repeat. `--auto` adds no new machinery — the
170
+ straight on to the next planned step. Repeat. `--auto` adds no new machinery; the
166
171
  `after`-agent output simply feeds the *existing* self-review halt condition.
167
172
  - **Stop and hand back to the human** the moment any of these is true: the check is red,
168
173
  the self-review finds a mismatch (surface exactly what, and do not checkpoint it), a
169
- bound agent returns `blocked` or `drift` (unblock-and-re-run, or `/plumbbob:refine` — an
174
+ bound agent returns `blocked` or `drift` (unblock-and-re-run, or `/plumbbob:refine`; an
170
175
  agent cannot advance the loop), a new decision is needed, no planned steps
171
176
  remain, or the top of a requested range is reached. **When `--auto` halts back to the
172
- human, give the same hand-off the default pause does** — run `plumbbob handoff` and
177
+ human, give the same hand-off the default pause does**; run `plumbbob handoff` and
173
178
  relay its block: the step just completed, the next undone step, and that next step's
174
179
  `- model:` recommendation if it has one, so a fresh context window knows which `/model`
175
180
  to select.
176
181
 
177
182
  `--auto` and a step range are the only paths that checkpoint without a human pause, and
178
183
  only because the human asked for it by name; a range re-imposes the pause at its top.
179
- The default — no flag, no range — always ends at the pause.
184
+ The default (no flag, no range) always ends at the pause.
180
185
 
181
186
  ### A step range (`N-M`) is a bounded `--auto`
182
187
 
183
188
  `/plumbbob:build 1-3` self-approves steps 1 through 3 exactly as `--auto` does, then **pauses
184
- after step 3** instead of chaining to done. The range *is* the opt-in — you do not also
189
+ after step 3** instead of chaining to done. The range *is* the opt-in; you do not also
185
190
  pass `--auto`. Run it as the `--auto` loop with one extra halt: **stop before building
186
- any next step whose plan number is past the top of the range.** It adds no machinery —
191
+ any next step whose plan number is past the top of the range.** It adds no machinery:
187
192
  just the one more entry already in the halt list above.
188
193
 
189
- - **`N-M` with N ≤ M** — build N…M, self-approving and checkpointing each, then pause at
194
+ - **`N-M` with N ≤ M**: build N…M, self-approving and checkpointing each, then pause at
190
195
  M. `N-N` is just the single step `N` (which already ends at the pause).
191
- - **N > M** (e.g. `3-1`) — that is not a range you can walk; report it and stop rather
196
+ - **N > M** (for example `3-1`): that is not a range you can walk; report it and stop rather
192
197
  than guess the intent.
193
- - **M past the last planned step** (e.g. `1-9` with three steps) — build through the
198
+ - **M past the last planned step** (for example `1-9` with three steps): build through the
194
199
  last planned step and stop; that is the existing "no planned steps remain" halt, not
195
200
  an error.
196
- - **A step inside the range won't build** (missing, or its seam won't parse) — `plumbbob
201
+ - **A step inside the range won't build** (missing, or its seam won't parse): `plumbbob
197
202
  build <n>` fails exactly as it always does; surface it and stop, the same as a red
198
203
  check. Do not skip past the gap.
199
- - **N is above the next undone step** — you are jumping over earlier planned work; note
204
+ - **N is above the next undone step**: you are jumping over earlier planned work; note
200
205
  that you are skipping the steps before N, then proceed (the same latitude as a
201
206
  single-number jump like `/plumbbob:build 4`).
202
207
 
@@ -205,27 +210,27 @@ just the one more entry already in the halt list above.
205
210
  - **Swappable, never required.** The loop works without this skill; `/plumbbob:verify`
206
211
  checkpoints a hand-built or vibed diff just the same.
207
212
  - **Build the decided step, not a new one.** Implement what `intent.md` settled. A
208
- new idea mid-build is captured with `plumbbob park`, not an edit — a promise to
213
+ new idea mid-build is captured with `plumbbob park`, not an edit: a promise to
209
214
  defer writes nothing; the park line does.
210
215
  - **Default ends at the pause.** Implement → verify → wait for approval; never
211
216
  checkpoint without it. Only an explicit `--auto` or a step range lets the agent approve
212
- in your place, and it still halts on a red check or any mismatch — a range also stops
217
+ in your place, and it still halts on a red check or any mismatch; a range also stops
213
218
  at its top.
214
219
  - **Agents feed the beat; they never advance it**. `before` loads context,
215
- `build` writes the diff, `after` is advisory — none can checkpoint, flip a step, or
220
+ `build` writes the diff, `after` is advisory; none can checkpoint, flip a step, or
216
221
  chain. `blocked` → unblock and re-run; `drift` → `/plumbbob:refine`. You are still the one
217
222
  who verifies and (bar `--auto`) the human is still the clock. See **§ Running bound
218
223
  agents** for the mechanics.
219
224
  - **A refused checkpoint is the pause, not an error.** Under plumbbob's turn
220
- hook a same-turn `checkpoint` is refused by design — present the diff and the closing
225
+ hook a same-turn `checkpoint` is refused by design: present the diff and the closing
221
226
  block, **end the turn**; the human's approval on their next turn is what lets you land
222
227
  it, and landing it is a deliberate beat, not a side effect of the next `/plumbbob:build`.
223
228
  Never route around it with a raw `git commit`. An explicit `/plumbbob:build --auto` or a typed
224
- step range in the human's own prompt are the only self-approvals — **never write `auto`
229
+ step range in the human's own prompt are the only self-approvals; **never write `auto`
225
230
  into a settings file yourself to unlock a checkpoint. A grant you mint is no grant (the
226
- latch ignores it — D67 (auto-not-a-grant)); ask the human to type
231
+ latch ignores it; [D67 (auto-not-a-grant)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d67)); ask the human to type
227
232
  `/plumbbob:build --auto` again instead.**
228
233
  - **Close with the next model.** When a step lands, run `plumbbob handoff` and relay its
229
- block — it cites the completed step and the next undone step, and names that next step's
234
+ block; it cites the completed step and the next undone step, and names that next step's
230
235
  `- model:` recommendation if it has one, which is what a fresh context window needs to
231
236
  pick the right `/model` before the next run. Guidance, never a gate.
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  name: doctor
3
- description: Diagnose the plugin install from inside a session — is plumbbob linked, are the skills and hook present, is there a collision. A thin trigger for `plumbbob doctor`. Matters most for a marketplace install, where the CLI is on PATH only inside Claude Code.
3
+ description: "Diagnose the plugin install from inside a session: is plumbbob linked, are the skills and hook present, is there a collision. A thin trigger for `plumbbob doctor`. Matters most for a marketplace install, where the CLI is on PATH only inside Claude Code."
4
4
  disable-model-invocation: true
5
5
  model: haiku
6
6
  allowed-tools: Bash(plumbbob doctor:*)
7
7
  ---
8
8
 
9
- # PlumbBob — doctor (the is-it-installed-right move)
9
+ # PlumbBob: doctor (the is-it-installed-right move)
10
10
 
11
11
  Install diagnostic (injected when this skill runs): !`plumbbob doctor 2>&1 || true`
12
12
 
13
- This is a **thin driver** for `plumbbob doctor`. The report above is read-only — `doctor`
13
+ This is a **thin driver** for `plumbbob doctor`. The report above is read-only: `doctor`
14
14
  inspects, it never writes. This skill carries **no Edit and no Write tool**: never edit a file
15
15
  to change what the report says. If a check failed, surface its `→ fix` verbatim and apply
16
16
  *that* fix, then have the human **restart Claude Code (or `/reload-plugins`)**. **Never retry.**
@@ -19,23 +19,23 @@ to change what the report says. If a check failed, surface its `→ fix` verbati
19
19
 
20
20
  The CLI's reach differs by install path, and that is the whole point of running `doctor` here:
21
21
 
22
- - **Marketplace plugin** — `plumbbob` is on PATH **only inside a Claude Code session** (its
23
- `bin/` shims are injected into the Bash tool while the plugin is enabled). There is **no
24
- terminal `plumbbob`** to run. So this skill — or letting me run `plumbbob doctor` in-session
25
- — is the *only* way to reach `doctor`. The fact that this skill loaded at all already proves
22
+ - **Marketplace plugin**: `plumbbob` is on PATH **only inside a Claude Code session** (its
23
+ `bin/` shims are injected into the Bash tool while the plugin is enabled). **No
24
+ terminal `plumbbob`** exists. So this skill (or letting me run `plumbbob doctor` in-session)
25
+ is the *only* way to reach `doctor`. The fact that this skill loaded at all already proves
26
26
  the plugin is enabled and the shim is on PATH.
27
- - **Skills-dir / global install** (`npm i -g plumbbob` + `plumbbob init`) — `plumbbob` is on
27
+ - **Skills-dir / global install** (`npm i -g plumbbob` + `plumbbob init`): `plumbbob` is on
28
28
  PATH everywhere, so terminal `plumbbob doctor` works too; this skill is just convenience.
29
29
 
30
30
  The one failure `doctor` **cannot** diagnose: a plugin that never loaded at all (no
31
- `/plumbbob:*` skill appears). That is a `/plugin` / `/reload-plugins` problem, not doctor's —
31
+ `/plumbbob:*` skill appears). That is a `/plugin` / `/reload-plugins` problem, not doctor's,
32
32
  and if you are reading this, that is not your situation.
33
33
 
34
34
  ## What it does
35
35
 
36
- 1. Surface the injected `doctor` report verbatim — every `✓` and any `✗` with its `→ fix`.
36
+ 1. Surface the injected `doctor` report verbatim: every `✓` and any `✗` with its `→ fix`.
37
37
  If nothing rendered above (a few headless `-p` sessions skip the injection), just run
38
- `plumbbob doctor` yourself — it is in your tools — and surface that.
38
+ `plumbbob doctor` yourself (it is in your tools) and surface that.
39
39
  2. If all checks passed, say so; if a skill still misbehaves, the fix is a restart, not a re-run.
40
40
  3. If a check failed, apply its named `→ fix`, then restart Claude Code (or `/reload-plugins`).
41
41
  4. If the report is empty or shows `command not found`, the CLI is not on PATH: for a