plumbbob 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +9 -0
  3. package/dist/cli-core.js +4 -0
  4. package/dist/lib/agents.js +498 -0
  5. package/dist/lib/intent.js +98 -8
  6. package/dist/lib/settings.js +17 -0
  7. package/dist/lib/sidecar.js +36 -0
  8. package/dist/verbs/agent.js +312 -0
  9. package/dist/verbs/checkpoint.js +2 -1
  10. package/dist/verbs/doctor.js +96 -3
  11. package/dist/verbs/status.js +64 -2
  12. package/node_modules/checkride/CHANGELOG.md +139 -0
  13. package/node_modules/checkride/LICENSE +21 -0
  14. package/node_modules/checkride/README.md +179 -0
  15. package/node_modules/checkride/dist/adapters.d.ts +56 -0
  16. package/node_modules/checkride/dist/adapters.d.ts.map +1 -0
  17. package/node_modules/checkride/dist/adapters.js +219 -0
  18. package/node_modules/checkride/dist/adapters.js.map +1 -0
  19. package/node_modules/checkride/dist/cli.d.ts +29 -0
  20. package/node_modules/checkride/dist/cli.d.ts.map +1 -0
  21. package/node_modules/checkride/dist/cli.js +227 -0
  22. package/node_modules/checkride/dist/cli.js.map +1 -0
  23. package/node_modules/checkride/dist/config.d.ts +74 -0
  24. package/node_modules/checkride/dist/config.d.ts.map +1 -0
  25. package/node_modules/checkride/dist/config.js +126 -0
  26. package/node_modules/checkride/dist/config.js.map +1 -0
  27. package/node_modules/checkride/dist/doctor.d.ts +76 -0
  28. package/node_modules/checkride/dist/doctor.d.ts.map +1 -0
  29. package/node_modules/checkride/dist/doctor.js +299 -0
  30. package/node_modules/checkride/dist/doctor.js.map +1 -0
  31. package/node_modules/checkride/dist/index.d.ts +18 -0
  32. package/node_modules/checkride/dist/index.d.ts.map +1 -0
  33. package/node_modules/checkride/dist/index.js +13 -0
  34. package/node_modules/checkride/dist/index.js.map +1 -0
  35. package/node_modules/checkride/dist/init.d.ts +64 -0
  36. package/node_modules/checkride/dist/init.d.ts.map +1 -0
  37. package/node_modules/checkride/dist/init.js +489 -0
  38. package/node_modules/checkride/dist/init.js.map +1 -0
  39. package/node_modules/checkride/dist/links.d.ts +23 -0
  40. package/node_modules/checkride/dist/links.d.ts.map +1 -0
  41. package/node_modules/checkride/dist/links.js +125 -0
  42. package/node_modules/checkride/dist/links.js.map +1 -0
  43. package/node_modules/checkride/dist/orchestrator.d.ts +96 -0
  44. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -0
  45. package/node_modules/checkride/dist/orchestrator.js +234 -0
  46. package/node_modules/checkride/dist/orchestrator.js.map +1 -0
  47. package/node_modules/checkride/package.json +76 -0
  48. package/node_modules/checkride/templates/flat/fallow.toml +26 -0
  49. package/node_modules/checkride/templates/flat/pnpm-workspace.yaml +6 -0
  50. package/node_modules/checkride/templates/flat/tsconfig.json +9 -0
  51. package/node_modules/checkride/templates/hybrid/fallow.toml +42 -0
  52. package/node_modules/checkride/templates/hybrid/pnpm-workspace.yaml +6 -0
  53. package/node_modules/checkride/templates/hybrid/tsconfig.json +12 -0
  54. package/node_modules/checkride/templates/monorepo/fallow.toml +44 -0
  55. package/node_modules/checkride/templates/monorepo/pnpm-workspace.yaml +7 -0
  56. package/node_modules/checkride/templates/monorepo/tsconfig.json +7 -0
  57. package/node_modules/checkride/templates/shared/cspell.json +35 -0
  58. package/node_modules/checkride/templates/shared/gitignore +10 -0
  59. package/node_modules/checkride/templates/shared/markdownlint-cli2.jsonc +11 -0
  60. package/node_modules/checkride/templates/shared/npmrc +1 -0
  61. package/node_modules/checkride/templates/shared/oxlintrc.json +33 -0
  62. package/node_modules/checkride/templates/shared/rules/no-class.yml +8 -0
  63. package/node_modules/checkride/templates/shared/rules/no-deep-sibling-import.yml +15 -0
  64. package/node_modules/checkride/templates/shared/rules/no-default-export.yml +9 -0
  65. package/node_modules/checkride/templates/shared/rules/require-js-extension.yml +21 -0
  66. package/node_modules/checkride/templates/shared/sgconfig.yml +2 -0
  67. package/node_modules/checkride/templates/shared/tsconfig.base.json +28 -0
  68. package/node_modules/checkride/templates/shared/vitest.config.ts.template +27 -0
  69. package/package.json +4 -1
  70. package/skills/pb-build/SKILL.md +42 -7
  71. package/skills/pb-finish/SKILL.md +1 -1
  72. package/skills/pb-harvest/SKILL.md +1 -1
  73. package/skills/pb-park/SKILL.md +1 -1
  74. package/skills/pb-plan/SKILL.md +25 -4
  75. package/skills/pb-refine/SKILL.md +1 -1
  76. package/skills/pb-revert/SKILL.md +1 -1
  77. package/skills/pb-spike/SKILL.md +1 -1
  78. package/skills/pb-status/SKILL.md +1 -1
  79. package/skills/pb-step/SKILL.md +10 -2
  80. package/skills/pb-verify/SKILL.md +21 -6
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["dist", "node_modules"],
9
+ "references": [
10
+ { "path": "./packages/core" }
11
+ ]
12
+ }
@@ -0,0 +1,44 @@
1
+ # Fallow: dead code, circular deps, duplication, complexity, architecture boundaries.
2
+ # Docs: https://docs.fallow.tools
3
+
4
+ entry = [
5
+ "apps/*/src/index.ts",
6
+ "libs/*/src/index.ts",
7
+ "apps/*/src/**/*.test.ts",
8
+ "libs/*/src/**/*.test.ts",
9
+ "apps/*/test/**/*.test.ts",
10
+ "libs/*/test/**/*.test.ts",
11
+ "test/**/*.test.ts",
12
+ ]
13
+
14
+ # CLI/tooling deps used via command line or config files, not imported from source.
15
+ ignoreDependencies = ["@ast-grep/cli"]
16
+
17
+ [rules]
18
+ unused-exports = "error"
19
+ unused-files = "error"
20
+ unused-dependencies = "error"
21
+ circular-dependencies = "error"
22
+ boundary-violation = "error"
23
+
24
+ [duplicates]
25
+ mode = "mild"
26
+ minTokens = 50
27
+
28
+ [health]
29
+ maxCyclomatic = 15
30
+ maxCognitive = 15
31
+
32
+ # Architecture boundaries: libs are reusable internals; apps are deployable
33
+ # leaves. libs may not import from apps.
34
+ [[boundaries.zones]]
35
+ name = "apps"
36
+ patterns = ["apps/*/src/**"]
37
+
38
+ [[boundaries.zones]]
39
+ name = "libs"
40
+ patterns = ["libs/*/src/**"]
41
+
42
+ [[boundaries.rules]]
43
+ from = "libs"
44
+ allow = ["libs"]
@@ -0,0 +1,7 @@
1
+ packages:
2
+ - 'apps/*'
3
+ - 'libs/*'
4
+
5
+ allowBuilds:
6
+ '@ast-grep/cli': true
7
+ fallow: true
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./libs/core" },
5
+ { "path": "./apps/{{name}}" }
6
+ ]
7
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "version": "0.2",
3
+ "language": "en",
4
+ "files": ["**/src/**/*.ts", "**/test/**/*.ts", "**/*.md"],
5
+ "ignorePaths": [
6
+ "**/node_modules/**",
7
+ "**/dist/**",
8
+ ".check/**",
9
+ "pnpm-lock.yaml",
10
+ "package.json",
11
+ "CHANGELOG.md"
12
+ ],
13
+ "words": [
14
+ "checkride",
15
+ "biome",
16
+ "cspell",
17
+ "devDeps",
18
+ "esbuild",
19
+ "eslint",
20
+ "fallow",
21
+ "knip",
22
+ "markdownlint",
23
+ "NodeNext",
24
+ "noemit",
25
+ "oxlint",
26
+ "oxlintrc",
27
+ "pnpm",
28
+ "sgconfig",
29
+ "stryker",
30
+ "tsbuildinfo",
31
+ "tsconfig",
32
+ "tsgolint",
33
+ "vitest"
34
+ ]
35
+ }
@@ -0,0 +1,10 @@
1
+ node_modules/
2
+ dist/
3
+ .check/
4
+ .fallow/
5
+ .stryker-tmp/
6
+ coverage/
7
+ *.log
8
+ *.tsbuildinfo
9
+ .DS_Store
10
+ .pnpm-store/
@@ -0,0 +1,11 @@
1
+ {
2
+ "config": {
3
+ "default": true,
4
+ "MD013": false,
5
+ "MD033": false,
6
+ "MD041": false,
7
+ "MD060": false
8
+ },
9
+ "globs": ["**/*.md"],
10
+ "ignores": ["**/node_modules/**", "dist/**", ".check/**", "CHANGELOG.md"]
11
+ }
@@ -0,0 +1 @@
1
+ save-exact=true
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["typescript", "unicorn", "import"],
4
+ "categories": {
5
+ "correctness": "error",
6
+ "suspicious": "error",
7
+ "perf": "warn",
8
+ "style": "off"
9
+ },
10
+ "options": {
11
+ "typeAware": true
12
+ },
13
+ "rules": {
14
+ "typescript/no-floating-promises": "error",
15
+ "typescript/no-misused-promises": "error",
16
+ "typescript/no-explicit-any": "error",
17
+ "typescript/await-thenable": "error",
18
+ "typescript/no-unnecessary-type-assertion": "error",
19
+ "typescript/prefer-nullish-coalescing": "error",
20
+ "typescript/prefer-optional-chain": "error",
21
+ "no-console": "warn",
22
+ "eqeqeq": "error"
23
+ },
24
+ "overrides": [
25
+ {
26
+ "files": ["**/*.test.ts"],
27
+ "rules": {
28
+ "typescript/no-unsafe-type-assertion": "off"
29
+ }
30
+ }
31
+ ],
32
+ "ignorePatterns": ["dist/**", ".check/**", ".claude/**", "node_modules/**"]
33
+ }
@@ -0,0 +1,8 @@
1
+ id: no-class
2
+ message: Prefer functional/procedural style over classes. Use modules and closures instead.
3
+ severity: error
4
+ language: typescript
5
+ rule:
6
+ any:
7
+ - kind: class_declaration
8
+ - kind: abstract_class_declaration
@@ -0,0 +1,15 @@
1
+ id: no-deep-sibling-import
2
+ message: |
3
+ Deep-module boundary: don't reach past a sibling module's index.
4
+ Use `import { ... } from '../<sibling>/index.js'` instead of
5
+ `'../<sibling>/<internal>.js'`.
6
+ severity: error
7
+ language: typescript
8
+ files:
9
+ - '**/src/**/*.ts'
10
+ rule:
11
+ all:
12
+ - kind: import_statement
13
+ - regex: "from\\s+['\"]\\.\\./[^/]+/[^'\"]+['\"]"
14
+ - not:
15
+ regex: "from\\s+['\"]\\.\\./[^/]+/index\\.js['\"]"
@@ -0,0 +1,9 @@
1
+ id: no-default-export
2
+ message: Avoid default exports. Named exports give agents stable, discoverable names.
3
+ severity: error
4
+ language: typescript
5
+ files:
6
+ - '**/src/**/*.ts'
7
+ rule:
8
+ any:
9
+ - pattern: export default $$$
@@ -0,0 +1,21 @@
1
+ id: require-js-extension
2
+ message: |
3
+ NodeNext resolution requires an explicit `.js` (or `.mjs` / `.json`) extension
4
+ on relative imports. Use `'./foo.js'`, not `'./foo'` or `'./foo.ts'`.
5
+ severity: error
6
+ language: typescript
7
+ files:
8
+ - '**/src/**/*.ts'
9
+ - '**/test/**/*.ts'
10
+ rule:
11
+ any:
12
+ - all:
13
+ - kind: import_statement
14
+ - regex: "from\\s+['\"]\\.{1,2}/"
15
+ - not:
16
+ regex: "from\\s+['\"]\\.{1,2}/[^'\"]*\\.(js|mjs|json)['\"]"
17
+ - all:
18
+ - kind: export_statement
19
+ - regex: "from\\s+['\"]\\.{1,2}/"
20
+ - not:
21
+ regex: "from\\s+['\"]\\.{1,2}/[^'\"]*\\.(js|mjs|json)['\"]"
@@ -0,0 +1,2 @@
1
+ ruleDirs:
2
+ - rules
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "lib": ["ES2024"],
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+
8
+ "strict": true,
9
+ "noUncheckedIndexedAccess": true,
10
+ "exactOptionalPropertyTypes": true,
11
+ "noImplicitOverride": true,
12
+ "noPropertyAccessFromIndexSignature": true,
13
+ "noFallthroughCasesInSwitch": true,
14
+
15
+ "isolatedModules": true,
16
+ "verbatimModuleSyntax": true,
17
+ "esModuleInterop": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "skipLibCheck": true,
20
+ "resolveJsonModule": true,
21
+ "types": ["node"],
22
+
23
+ "composite": true,
24
+ "declaration": true,
25
+ "declarationMap": true,
26
+ "sourceMap": true
27
+ }
28
+ }
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ['**/src/**/*.{test,spec}.ts', '**/test/**/*.{test,spec}.ts'],
6
+ exclude: ['**/node_modules/**', '**/dist/**'],
7
+ reporters: ['default'],
8
+ coverage: {
9
+ provider: 'v8',
10
+ include: ['**/src/**/*.ts'],
11
+ exclude: [
12
+ '**/src/**/*.{test,spec}.ts',
13
+ '**/src/**/*.d.ts',
14
+ '**/node_modules/**',
15
+ '**/dist/**',
16
+ ],
17
+ reporter: ['text', 'json-summary', 'json'],
18
+ reportsDirectory: '.check/coverage',
19
+ thresholds: {
20
+ lines: 70,
21
+ functions: 70,
22
+ branches: 70,
23
+ statements: 70,
24
+ },
25
+ },
26
+ },
27
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plumbbob",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "Attention-first build process: 12 skills + a CLI that keep you the decider — guidance, not enforcement.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -56,6 +56,9 @@
56
56
  "dependencies": {
57
57
  "checkride": "0.1.6"
58
58
  },
59
+ "bundleDependencies": [
60
+ "checkride"
61
+ ],
59
62
  "scripts": {
60
63
  "clean": "rm -rf dist",
61
64
  "build": "tsc -p tsconfig.build.json",
@@ -4,12 +4,12 @@ description: The optional engine — read the next planned step from intent, imp
4
4
  argument-hint: "[step-number] [--auto]"
5
5
  disable-model-invocation: true
6
6
  model: opus
7
- allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob build:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(git diff:*)
7
+ allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob build:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(plumbbob agent:*), Bash(git diff:*)
8
8
  ---
9
9
 
10
10
  # PlumbBob — build a step (the optional engine)
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  This is the **bundled executor** — one way to turn a planned step into code. It is
15
15
  **optional** (D3): you can implement any step by hand, in a vibe session, or with
@@ -32,17 +32,43 @@ at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick
32
32
  3. **Read the plan.** Read the step's **done-when**, its **seam**, and the
33
33
  **Decisions** and **Constraints** in `intent.md`. Build to *that* — the deciding
34
34
  already happened, off the chat.
35
+ - **Run any bound `before`-agents** *(D43/D59)*. If the build's `harness.json` binds
36
+ agents to this step's `before` slot, run `plumbbob agent run --step <n> --mode
37
+ before` first: each returns a validated envelope on stdout that plumbbob also
38
+ appends to the step's `handoff.json`, and its `summary`/`body` become **context you
39
+ read into the build** (that is the whole point of the slot — load the context in
40
+ before you write). No binding, or no harness, is a clean no-op — just build.
35
41
  4. **Implement** the step, and only that step, staying within the declared seam. A
36
42
  new problem or "ooh what if" that surfaces mid-build is a `/pb-park`, **not** an
37
43
  edit — capture it and stay on the step. If you genuinely cannot finish without
38
44
  touching more than the seam, that is scope drift: surface it to the human rather
39
45
  than sprawling.
46
+ - **If a `build`-slot agent is bound, delegate the diff to it** *(D43)*. Run
47
+ `plumbbob agent run --step <n> --mode build` and let that agent author the step's
48
+ code instead of writing it yourself; its envelope reports what it did. You still
49
+ own the verify tick below — the diff is reviewed the same way whoever wrote it (D3).
50
+ - **A manifest's `when` prose is your cue to fire an agent mid-build** *(D43/D55)*.
51
+ The three slots are the only *declarative* lifecycle points; there is no config for
52
+ "a salient moment in the middle." That is judgment, and you are the frontier model
53
+ in the room: when the work reaches the situation a bound agent's `when` (or a step
54
+ `note`) describes, fire `plumbbob agent run <name> --step <n>` yourself. Prose is
55
+ the orchestration language; you are the workflow engine.
56
+ - **Route a non-`done` envelope by its status** *(D52)*. An agent that returns
57
+ `blocked` couldn't finish (missing input, failed precondition): surface its `notes`,
58
+ let the human unblock, and re-run it — don't work around it. One that returns
59
+ `drift` finished but found the plan no longer matches reality: **stop and send the
60
+ human to `/pb-refine`** to repair the plan before continuing, rather than building
61
+ on a plan that's now wrong. A non-zero exit is a failed run: report it and stop.
40
62
  5. **Verify, through to the pause.** Run the verify tick exactly as `/pb-verify`
41
63
  does: `plumbbob check` (on red, read `.check/summary.json` and the failing slot's
42
64
  raw output under `.check/` for the actual diagnostics; while iterating on a fix,
43
65
  narrow the loop with `plumbbob check --bail --only <slots>` — the checkpoint gate
44
- still runs everything) → self-review the diff against the done-when, the
45
- Decisions, and the Constraints (a single structured read, D16) validate **PAUSE
66
+ still runs everything) → run any bound `after`-agents (`plumbbob agent run --step
67
+ <n> --mode after`) and fold their envelopes into the self-review as **advisory
68
+ input** — they inform, they never gate (checkride gates, the human is the clock; an
69
+ `after`-agent that could fail a step is the lock in autonomy's costume, D45) →
70
+ self-review the diff against the done-when, the Decisions, and the Constraints (a
71
+ single structured read, D16) → validate → **PAUSE
46
72
  for the human's approval** → only on approval, checkpoint with
47
73
  `plumbbob checkpoint <n> --body <<'BODY' … BODY` — a commit body **proportional to the
48
74
  step** (a line for a trivial change, a short paragraph for a meatier one; no TIL scan,
@@ -57,12 +83,17 @@ at the pause for your approval. **Re-firing `/pb-build` is itself the clock tick
57
83
  progress instead of approving each step. It does the same work, but **the agent reviews
58
84
  and approves in the human's place**, and it **chains**:
59
85
 
60
- - Build the next step `check` self-review **if the check is green AND the
86
+ - Build the next step, running its slots in the same order as the default path
87
+ (D56): bound `before`-agents → implement (or the bound `build`-agent) → bound
88
+ `after`-agents → `check` → self-review → **if the check is green AND the
61
89
  self-review finds no done-when / Decision / Constraint mismatch, checkpoint** and move
62
- straight on to the next planned step. Repeat.
90
+ straight on to the next planned step. Repeat. `--auto` adds no new machinery — the
91
+ `after`-agent output simply feeds the *existing* self-review halt condition.
63
92
  - **Stop and hand back to the human** the moment any of these is true: the check is red,
64
93
  the self-review finds a mismatch (surface exactly what, and do not checkpoint it), a
65
- new decision is needed, or no planned steps remain.
94
+ bound agent returns `blocked` or `drift` (unblock-and-re-run, or `/pb-refine` an
95
+ agent cannot advance the loop, C6), a new decision is needed, or no planned steps
96
+ remain.
66
97
 
67
98
  `--auto` is the only path that checkpoints without a human pause, and only because the
68
99
  human asked for it by name. The default — no flag — always ends at the pause.
@@ -76,3 +107,7 @@ human asked for it by name. The default — no flag — always ends at the pause
76
107
  - **Default ends at the pause.** Implement → verify → wait for approval; never
77
108
  checkpoint without it. Only an explicit `--auto` lets the agent approve in your place,
78
109
  and it still halts on a red check or any mismatch.
110
+ - **Agents feed the beat; they never advance it** (C6/D45). `before` loads context,
111
+ `build` writes the diff, `after` is advisory — none can checkpoint, flip a step, or
112
+ chain. `blocked` → unblock and re-run; `drift` → `/pb-refine`. You are still the one
113
+ who verifies and (bar `--auto`) the human is still the clock.
@@ -8,7 +8,7 @@ allowed-tools: Read, Write, Bash(plumbbob status:*), Bash(plumbbob finish:*)
8
8
 
9
9
  # PlumbBob — finish the build (the close-out)
10
10
 
11
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
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
13
  `/pb-finish` ends the build: it captures what happened, then makes the final commit
14
14
  that closes the session. **Report by default** (D9) — no refuse-without-report gate,
@@ -8,7 +8,7 @@ allowed-tools: Read, Edit, Bash(plumbbob status:*)
8
8
 
9
9
  # PlumbBob — harvest the park list
10
10
 
11
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
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
13
  `/pb-harvest` is the complement of `/pb-park` (D7): you parked ideas as seeds during a
14
14
  build; now, at a boundary, you harvest them — decide what each one is.
@@ -9,7 +9,7 @@ allowed-tools: Bash(plumbbob status:*), Bash(plumbbob park:*)
9
9
 
10
10
  # PlumbBob — park an idea (capture, don't chase)
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  `/pb-park` is the **capture** half of the loop; `/pb-harvest` is where parked items
15
15
  get triaged later (D7). Capturing the instant an idea arrives — instead of acting on
@@ -4,12 +4,12 @@ description: "Frame a fresh goal and author the whole plan — Frame, Decisions,
4
4
  argument-hint: "[spec-path | intent]"
5
5
  disable-model-invocation: true
6
6
  model: opus
7
- allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob start:*), Bash(plumbbob checkpoint:*)
7
+ allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob start:*), Bash(plumbbob checkpoint:*), Bash(plumbbob agent list:*)
8
8
  ---
9
9
 
10
10
  # PlumbBob — plan a goal (the whole-goal move)
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  `/pb-plan` is the **whole-goal** move — it opens a session and gets the deciding out
15
15
  of your head and onto `intent.md` *before* any code. By default it authors the
@@ -60,14 +60,35 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
60
60
  paths it touches). Later steps may be fuzzier than the first — that's fine; they get
61
61
  sharpened just-in-time when you reach them with `/pb-step`. Keep each small enough to
62
62
  verify in one review pass.
63
- 5. **Commit the plan.** Once the human approves the frame and steps, run
63
+ 5. **Offer harness bindings** *(optional D42/D43)*. If the build will lean on
64
+ user-authored agents, author `harness.json` in the build folder (beside `intent.md`)
65
+ and review it at the **same plan pause**, alongside the steps — bindings are
66
+ plan-adjacent configuration, so they converge with the plan. It binds agents to a
67
+ step's three lifecycle slots — `before` (context in), `build` (the diff), `after`
68
+ (advisory review) — with an optional prose `note`; a `defaults` block binds every
69
+ step. Run `plumbbob agent list` to see what's resolvable, per step:
70
+
71
+ ```json
72
+ {
73
+ "contract": 1,
74
+ "defaults": { "after": ["reviewer"] },
75
+ "steps": { "3": { "before": ["context-loader"], "note": "watch the auth seam" } }
76
+ }
77
+ ```
78
+
79
+ Keep it **bindings + prose only, never a conditional** (C3): the file says *which*
80
+ agent, not *when* — the host model reads each manifest's `when` prose and a step's
81
+ `note` and decides when to fire one mid-build. Skip the file entirely when no step
82
+ uses an agent — the loop runs identically without it (D54). The plan commit picks it
83
+ up automatically (it lives in the build folder).
84
+ 6. **Commit the plan.** Once the human approves the frame and steps, run
64
85
  `plumbbob checkpoint --plan` to commit the scaffold on its own — subject
65
86
  `plumbbob: plan — <title>`, only `.plumbbob/builds/<slug>/`, a `plan <sha>` line in
66
87
  `checkpoints` (D36). This keeps the first step's diff clean, so history reads
67
88
  baseline → plan → steps. Pass a proportional `--body` (the single-quoted stdin
68
89
  heredoc) when the rationale is worth carrying; skip it for a small plan. Do this
69
90
  only on the human's approval — the plan is their convergence.
70
- 6. **Offer to stress-test it.** Suggest `/pb-refine` to attack the frame for holes (or
91
+ 7. **Offer to stress-test it.** Suggest `/pb-refine` to attack the frame for holes (or
71
92
  to repair the plan as it drifts). Optional, the human's call.
72
93
 
73
94
  ## The interview (mode 1)
@@ -9,7 +9,7 @@ allowed-tools: Read, Edit, Bash(plumbbob status:*)
9
9
 
10
10
  # PlumbBob — refine the plan
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  `/pb-refine` keeps `intent.md` honest. Use it at **any point** — right after `/pb-plan`
15
15
  to stress-test a fresh frame, or mid-build to repair a plan that drifted from what the
@@ -9,7 +9,7 @@ allowed-tools: Bash(plumbbob status:*), Bash(plumbbob revert:*)
9
9
 
10
10
  # PlumbBob — revert to a checkpoint (driver)
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  This is a **driver skill** — a chat-side trigger for the mechanical `plumbbob revert` verb, so the whole workflow runs from the agent window instead of a terminal. It is `disable-model-invocation: true`: only the human fires it. It carries **no Edit and no Write tool** — its only action is to shell the verb and report the verb's output verbatim, including any refusal. The CLI is the source of truth: never retry a refused transition, and never edit a file to work around one.
15
15
 
@@ -9,7 +9,7 @@ allowed-tools: Bash(plumbbob status:*), Bash(plumbbob spike:*)
9
9
 
10
10
  # PlumbBob — spike an experiment (driver)
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  This is a **driver skill** — a chat-side trigger for the mechanical `plumbbob spike` verb, so the whole workflow runs from the agent window instead of a terminal. It is `disable-model-invocation: true`: only the human fires it. It carries **no Edit and no Write tool** — its only action is to shell the verb and report the verb's output verbatim, including any refusal. The CLI is the source of truth: never retry a refused transition, and never edit a file to work around one.
15
15
 
@@ -8,7 +8,7 @@ allowed-tools: Bash(plumbbob status:*)
8
8
 
9
9
  # PlumbBob — orient (the where-am-I move)
10
10
 
11
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
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
13
  This is a **thin driver** for `plumbbob status`. The dashboard above is your
14
14
  orientation — the intent, the step list, the parked/open-question counts, and the
@@ -4,12 +4,12 @@ description: Revise the next increment just-in-time — sharpen the next undone
4
4
  argument-hint: "[what-changed]"
5
5
  disable-model-invocation: true
6
6
  model: opus
7
- allowed-tools: Read, Edit, Bash(plumbbob status:*)
7
+ allowed-tools: Read, Edit, Write, Bash(plumbbob status:*), Bash(plumbbob agent list:*)
8
8
  ---
9
9
 
10
10
  # PlumbBob — revise the next step (the single-increment move)
11
11
 
12
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
12
+ 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."`
13
13
 
14
14
  `/pb-plan` authors the **whole** step list up front, so `/pb-step` is the
15
15
  **just-in-time revision** move: it keeps the *next* undone step honest against what the
@@ -40,6 +40,14 @@ grew — but its everyday job is to sharpen, not to invent.
40
40
  3. **Get the human's OK**, then write it into `## Steps` in the standard format —
41
41
  `N. [ ] <title> — **done when:** <criterion>` with a `- seam:` sub-line. Revise the
42
42
  existing step in place; only append when you are genuinely adding an increment.
43
+ 4. **Revise the step's harness bindings if they drifted too** *(optional — D42)*. If the
44
+ build carries a `harness.json` (beside `intent.md`) and the reality that moved the
45
+ step also changed which agents it wants, sharpen that step's slot bindings
46
+ (`before`/`build`/`after`) and `note` at the same time — this is the just-in-time
47
+ counterpart to `/pb-plan`'s plan-time binding. `plumbbob agent list` shows what's
48
+ resolvable. Same rule as the plan move: bindings + prose only, never a conditional
49
+ (C3). Leave it untouched when the step's agents are still right, or when the build
50
+ uses none.
43
51
 
44
52
  ## The hard contracts
45
53
 
@@ -3,12 +3,12 @@ name: pb-verify
3
3
  description: "The verify tick — run the check, self-review the diff against intent, validate the step's done-when, pause for your approval, then checkpoint. Executor-agnostic: it reads the diff, not who wrote it."
4
4
  disable-model-invocation: true
5
5
  model: opus
6
- allowed-tools: Read, Bash(plumbbob status:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(git diff:*), Bash(git status:*)
6
+ allowed-tools: Read, Bash(plumbbob status:*), Bash(plumbbob check:*), Bash(plumbbob checkpoint:*), Bash(plumbbob agent:*), Bash(git diff:*), Bash(git status:*)
7
7
  ---
8
8
 
9
9
  # PlumbBob — verify a step (the tick)
10
10
 
11
- Current session state (injected when this skill runs): !`plumbbob status 2>/dev/null || echo "plumbbob CLI not found - install the dep and re-run: npm i -g plumbbob && plumbbob init"`
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
13
  This is the **tick** — the one beat where the human is the clock. Whatever produced
14
14
  the current diff — `/pb-build`, your own hands, a vibe session, another harness —
@@ -24,17 +24,29 @@ this skill verifies it the same way: **it reads the diff, not the author** (D3).
24
24
  scrollback. Report what failed and do **not** pause for approval — there is
25
25
  nothing to approve yet. The human fixes it and re-invokes. (Exit 2 means the gate
26
26
  itself broke — a harness problem to surface, not a code failure.)
27
- 2. **Self-review** *(a single structured read, D16)*. Read `git diff` and
27
+ 2. **Run any bound `after`-agents** *(optional D45)*. If the build's `harness.json`
28
+ binds agents to this step's `after` slot, run `plumbbob agent run --step <n> --mode
29
+ after`. Their envelopes are **advisory input to the self-review, never a gate** —
30
+ `plumbbob check` already gated in step 1, and an `after`-agent that could fail a step
31
+ is the lock returning in autonomy's costume (D45). Fold a `done` envelope's
32
+ `summary`/`body` into the review below; route a non-`done` one by its status (D52): a
33
+ `blocked` agent couldn't finish — surface its `notes`, let the human unblock, re-run;
34
+ a `drift` agent found the plan no longer matches reality — stop and send the human to
35
+ `/pb-refine` to repair the plan before checkpointing. No binding, or no harness, is a
36
+ clean no-op.
37
+ 3. **Self-review** *(a single structured read, D16)*. Read `git diff` and
28
38
  `.plumbbob/intent.md`, then in one pass check the diff against:
29
39
  - the current step's **done-when** criterion — is it actually met?
30
40
  - the **Decisions** — does anything contradict a settled call?
31
41
  - the **Constraints** — are any violated?
42
+ - any **`after`-agent findings** from step 2 — advisory, weigh them, don't defer to them.
32
43
  Surface every mismatch plainly. You are reviewing, not building — do not fix anything.
33
- 3. **Validate.** State, yes or no, whether the step's done-when is met, with the evidence.
34
- 4. **PAUSE.** Present the check result, the self-review, and the validation, then
44
+ 4. **Validate.** State, yes or no, whether the step's done-when is met, with the evidence.
45
+ 5. **PAUSE.** Present the check result, the self-review (with any `after`-agent
46
+ findings), and the validation, then
35
47
  **stop and wait for the human's explicit approval.** This is the convergence beat;
36
48
  the human is the clock. Never checkpoint without it.
37
- 5. **Checkpoint** *(only after approval)*. Run `plumbbob checkpoint`: it makes the WIP
49
+ 6. **Checkpoint** *(only after approval)*. Run `plumbbob checkpoint`: it makes the WIP
38
50
  commit, records the SHA, flips the step to done, appends the step to the build-log's
39
51
  `## Log`, and returns to DESIGN. The CLI owns the commit **subject**
40
52
  (`plumbbob: step N — <title>`); you own the **body**. Compose a body *proportional to
@@ -62,3 +74,6 @@ this skill verifies it the same way: **it reads the diff, not the author** (D3).
62
74
  it and end your turn.
63
75
  - **You review; you do not build.** If the self-review finds a problem, surface it
64
76
  and stop — fixing is a new build beat, not part of verify.
77
+ - **`after`-agents advise; they never gate** (D45). Their output feeds the
78
+ self-review — checkride gates, the human approves. `blocked` → unblock and re-run;
79
+ `drift` → `/pb-refine` before checkpointing. No code path makes them blocking.