plumbbob 0.12.1 → 0.13.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +20 -13
- package/dist/cli-core.js +22 -6
- package/dist/lib/git.js +20 -8
- package/dist/lib/latch.js +23 -5
- package/dist/lib/notice.js +18 -3
- package/dist/lib/orient.js +350 -34
- package/dist/lib/sidecar.js +202 -3
- package/dist/lib/steplist.js +128 -0
- package/dist/verbs/abandon.js +19 -14
- package/dist/verbs/build.js +73 -14
- package/dist/verbs/checkpoint.js +31 -21
- package/dist/verbs/doctor.js +23 -4
- package/dist/verbs/finish.js +21 -17
- package/dist/verbs/handoff.js +259 -80
- package/dist/verbs/order.js +32 -20
- package/dist/verbs/park.js +6 -2
- package/dist/verbs/recover.js +31 -1
- package/dist/verbs/relay.js +306 -0
- package/dist/verbs/revert.js +17 -11
- package/dist/verbs/spike.js +50 -38
- package/dist/verbs/start.js +13 -9
- package/dist/verbs/status.js +2 -2
- package/dist/verbs/turn.js +90 -53
- package/dist/verbs/use.js +21 -17
- package/hooks/hooks.json +7 -0
- package/package.json +1 -1
- package/skills/build/SKILL.md +107 -49
- package/skills/harvest/SKILL.md +10 -1
- package/skills/order/SKILL.md +20 -10
- package/skills/park/SKILL.md +7 -0
- package/skills/plan/SKILL.md +52 -7
- package/skills/refine/SKILL.md +21 -3
- package/skills/status/SKILL.md +8 -3
- package/skills/step/SKILL.md +16 -3
- package/skills/verify/SKILL.md +35 -18
- package/templates/intent.md +23 -5
package/dist/verbs/start.js
CHANGED
|
@@ -10,7 +10,7 @@ import { readTemplate, stampTemplate } from "../lib/templates.js";
|
|
|
10
10
|
import { findRepoRoot, hasCommit, headSha, isDirty, isIgnored } from "../lib/git.js";
|
|
11
11
|
import { sidecarDir, buildDir, listBuilds, slugify, checkpointsPath, intentPath, buildLogPath, beginSession, hasSession, excludeControl, excludeSidecar, setGrant, stampTick, } from "../lib/sidecar.js";
|
|
12
12
|
import { settingsPath } from "../lib/settings.js";
|
|
13
|
-
import { advisory, ending, notice, transition } from "../lib/notice.js";
|
|
13
|
+
import { advisory, ending, notice, printEnding, transition } from "../lib/notice.js";
|
|
14
14
|
/**
|
|
15
15
|
* Human-readable echo of the default check gate, stamped into the templates'
|
|
16
16
|
* {{CHECK}} line.
|
|
@@ -177,15 +177,19 @@ export async function start(cwd, args) {
|
|
|
177
177
|
// No pointer: the plan skill owns the turn that follows, and there is no step
|
|
178
178
|
// yet to aim at, so the ending closes on the remedy line that says where to
|
|
179
179
|
// frame the build.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
printEnding(root, {
|
|
181
|
+
tier: 'driver',
|
|
182
|
+
render: 'start',
|
|
183
|
+
text: ending({
|
|
184
|
+
lead: transition({
|
|
185
|
+
label: 'Session',
|
|
186
|
+
fact: `started "${title}"`,
|
|
187
|
+
detail: [`baseline ${sha.slice(0, 9)}`],
|
|
188
|
+
remedy: `frame and decide in ${intentLocation}, then build a step`,
|
|
189
|
+
}),
|
|
190
|
+
advisories,
|
|
186
191
|
}),
|
|
187
|
-
|
|
188
|
-
}));
|
|
192
|
+
});
|
|
189
193
|
return 0;
|
|
190
194
|
}
|
|
191
195
|
/**
|
package/dist/verbs/status.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// behavior, so the `NO ACTIVE SESSION` sentinel is kept exact.
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
|
-
import { commitsSince, findRepoRoot } from "../lib/git.js";
|
|
9
|
+
import { PLAN_COMMIT_MARKER, commitsSince, findRepoRoot } from "../lib/git.js";
|
|
10
10
|
import { SLOTS, readHarnessFile, resolveAgent, } from "../lib/agents.js";
|
|
11
11
|
import { buildFolder, buildLogPath, checkpointsPath, hasSession, inSpike, intentPath, listBuilds, resolveBuild, stepPath, } from "../lib/sidecar.js";
|
|
12
12
|
import { formatOrientation, lastLedgerSha, orient, parseRequestedStep } from "../lib/orient.js";
|
|
@@ -80,7 +80,7 @@ export function status(cwd, args = []) {
|
|
|
80
80
|
inFlight: /^\d+$/.test(inFlightRaw) ? Number(inFlightRaw) : null,
|
|
81
81
|
spiking: inSpike(root, slug),
|
|
82
82
|
requested: parseRequestedStep(invoked),
|
|
83
|
-
outOfBand: anchor === null ? 0 : commitsSince(root, anchor),
|
|
83
|
+
outOfBand: anchor === null ? 0 : commitsSince(root, anchor, PLAN_COMMIT_MARKER),
|
|
84
84
|
});
|
|
85
85
|
const lines = [formatOrientation(orientation), ...harnessSection(root, slug)];
|
|
86
86
|
process.stdout.write(`${lines.join('\n')}\n`);
|
package/dist/verbs/turn.js
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
// `plumbbob turn`: the turn-ledger hook. Wired to the harness's
|
|
2
|
-
// UserPromptSubmit event, it runs once per
|
|
2
|
+
// UserPromptSubmit event, it runs once per prompt and maintains two of the
|
|
3
3
|
// sidecar's git-excluded control files (flat files under `.plumbbob/`): it ticks
|
|
4
4
|
// `TURN`, the monotonic count of human turns the checkpoint latch compares
|
|
5
5
|
// against a step's entry stamp to know a human turn intervened since the step
|
|
6
6
|
// began, and it rewrites `GRANT` from the literal prompt, minting a one-turn
|
|
7
|
-
// self-approval only when the human typed `/build --auto` or
|
|
8
|
-
//
|
|
7
|
+
// self-approval only when the human typed `/build --auto` or a step range
|
|
8
|
+
// (`N-M`, or a list such as `20,5-9`), clearing it otherwise (those strings can only come from a human
|
|
9
9
|
// keystroke, because `build` is disable-model-invocation: a grant the model
|
|
10
|
-
// could forge would be no grant).
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
10
|
+
// could forge would be no grant). The harness also sends a background job's
|
|
11
|
+
// completion notice through this event, as a prompt the human never typed, so
|
|
12
|
+
// a notice ticks nothing, keeps the grant, and clears only the ending `RELAY`
|
|
13
|
+
// holds from an earlier turn. When a step is in flight a typed prompt also
|
|
14
|
+
// emits ONE `UserPromptSubmit` additionalContext line so a fresh session
|
|
15
|
+
// (post-compaction, or a scripted `-p` turn where no skill prose is in context)
|
|
16
|
+
// still knows the beat: a tangent is parked, not chased, and a message that
|
|
17
|
+
// also asks something is answered before the park's block. Guidance only: it
|
|
18
|
+
// blocks nothing. It ALWAYS exits 0 and any failure degrades to no output, so
|
|
19
|
+
// neither the tick nor the emit can ever wedge prompting.
|
|
16
20
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
-
import { findSessionRoot, setGrant, stepPath, turnPath } from "../lib/sidecar.js";
|
|
21
|
+
import { clearRelay, findSessionRoot, notePrompt, readPrompt, setGrant, stepPath, turnPath, } from "../lib/sidecar.js";
|
|
22
|
+
import { normalizeStepList, readBuildArgs } from "../lib/steplist.js";
|
|
18
23
|
/**
|
|
19
|
-
* The hook entry point: apply the
|
|
24
|
+
* The hook entry point: apply the prompt, then emit the in-flight-step nudge
|
|
25
|
+
* when it counted as a human turn.
|
|
20
26
|
*
|
|
21
27
|
* Always returns 0: a broken turn must never wedge a prompt, so any failure is
|
|
22
28
|
* swallowed and degrades to no output.
|
|
23
29
|
*/
|
|
24
30
|
export function turn(cwd, _args) {
|
|
25
31
|
try {
|
|
26
|
-
applyTurn(cwd, readStdin())
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if (applyTurn(cwd, readStdin())) {
|
|
33
|
+
const context = stepInFlightContext(cwd);
|
|
34
|
+
if (context !== null)
|
|
35
|
+
process.stdout.write(context);
|
|
36
|
+
}
|
|
30
37
|
}
|
|
31
38
|
catch {
|
|
32
39
|
// A broken turn must never wedge a prompt: swallow and exit 0.
|
|
@@ -39,8 +46,10 @@ export function turn(cwd, _args) {
|
|
|
39
46
|
* A step is in flight when a STEP marker (the flat control file recording the
|
|
40
47
|
* step between `build` and `checkpoint`) is present. Guidance only: it reminds a
|
|
41
48
|
* fresh session that a tangent is a park (not an edit), that the park's own
|
|
42
|
-
* block is the whole turn
|
|
43
|
-
*
|
|
49
|
+
* block is the whole turn when the message is only a tangent, that a message
|
|
50
|
+
* which also asks something is answered first with the block after the answer,
|
|
51
|
+
* and how the step lands, since neither the build nor the park prose may be in
|
|
52
|
+
* context after compaction or on a scripted turn.
|
|
44
53
|
* Exported because an in-process test cannot read the hook's real stdout.
|
|
45
54
|
*/
|
|
46
55
|
export function stepInFlightContext(cwd) {
|
|
@@ -53,6 +62,9 @@ export function stepInFlightContext(cwd) {
|
|
|
53
62
|
const text = `plumbbob: step ${step} is in flight — a new idea or tangent is a park, not an edit: ` +
|
|
54
63
|
`capture it with \`plumbbob park "<one line>"\` and relay the block it prints whole, ` +
|
|
55
64
|
`with nothing written around it, since that block is the whole turn. ` +
|
|
65
|
+
`The one exception is a message that also asks something: answer the question first, ` +
|
|
66
|
+
`then relay the park's block after the answer and write nothing after it. ` +
|
|
67
|
+
`The block's pointer back at the step closes the turn either way, so run no \`plumbbob handoff\` after it. ` +
|
|
56
68
|
`The step stays open and lands when you run \`plumbbob checkpoint ${step}\` after approval.`;
|
|
57
69
|
return `${JSON.stringify({ hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: text } })}\n`;
|
|
58
70
|
}
|
|
@@ -72,7 +84,17 @@ function inFlightStep(root) {
|
|
|
72
84
|
}
|
|
73
85
|
}
|
|
74
86
|
/**
|
|
75
|
-
* The testable core: apply one
|
|
87
|
+
* The testable core: apply one prompt given the raw hook stdin, and say
|
|
88
|
+
* whether it counted as a human turn.
|
|
89
|
+
*
|
|
90
|
+
* A typed prompt ticks TURN and rewrites GRANT. A notice touches neither,
|
|
91
|
+
* because the latch reads TURN passing a step's entry stamp as its one sign
|
|
92
|
+
* that a human answered, and a grant the human typed has to outlive a job
|
|
93
|
+
* that reports back partway through the range. A notice that starts a turn of
|
|
94
|
+
* its own clears RELAY, whose ending was judged when its own turn ended; one
|
|
95
|
+
* the harness folds into the running turn arrives under that turn's prompt id
|
|
96
|
+
* and leaves RELAY alone, since the ending the turn just printed has yet to be
|
|
97
|
+
* judged.
|
|
76
98
|
*
|
|
77
99
|
* Split from the fd-0 read so the unit tests can drive it directly: an
|
|
78
100
|
* in-process test cannot feed fd 0 (the same constraint checkpoint's `--body`
|
|
@@ -81,10 +103,35 @@ function inFlightStep(root) {
|
|
|
81
103
|
export function applyTurn(cwd, raw) {
|
|
82
104
|
const root = findSessionRoot(cwd);
|
|
83
105
|
if (root === null)
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
106
|
+
return false; // no active session above cwd: silent no-op.
|
|
107
|
+
const { prompt, promptId } = readPayload(raw);
|
|
108
|
+
const notice = isNotice(prompt);
|
|
109
|
+
if (notice) {
|
|
110
|
+
if (promptId === null || promptId !== readPrompt(root))
|
|
111
|
+
clearRelay(root);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
writeFileSync(turnPath(root), `${readTurn(root) + 1}\n`);
|
|
115
|
+
setGrant(root, grantFromPrompt(prompt));
|
|
116
|
+
}
|
|
117
|
+
// Last, so a record that cannot be written never costs the tick or the grant.
|
|
118
|
+
notePrompt(root, promptId);
|
|
119
|
+
return !notice;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Whether a prompt is the harness reporting on background work rather than
|
|
123
|
+
* the human typing.
|
|
124
|
+
*
|
|
125
|
+
* Nothing in the payload marks one, but every notice opens on a frame of the
|
|
126
|
+
* harness's own at its very first character: `<task-notification>` for a
|
|
127
|
+
* finished shell job, a Monitor event, or a subagent's task, and
|
|
128
|
+
* `<agent-message ` for a subagent's or another session's report. A misread
|
|
129
|
+
* errs safe: a typed prompt taken for a notice costs one tick and one grant,
|
|
130
|
+
* so the latch only grows stricter, while a notice taken for a typed prompt
|
|
131
|
+
* is a turn no human took.
|
|
132
|
+
*/
|
|
133
|
+
export function isNotice(prompt) {
|
|
134
|
+
return prompt.startsWith('<task-notification>') || prompt.startsWith('<agent-message ');
|
|
88
135
|
}
|
|
89
136
|
/**
|
|
90
137
|
* Derive the GRANT content from the human's prompt, or null to clear it.
|
|
@@ -92,9 +139,12 @@ export function applyTurn(cwd, raw) {
|
|
|
92
139
|
* A grant is minted only from a literal `build` invocation the human typed:
|
|
93
140
|
* `build` is disable-model-invocation, so this string can only come from a
|
|
94
141
|
* human keystroke, never model-authored pressure: a grant the model can forge
|
|
95
|
-
* is no grant. A
|
|
96
|
-
*
|
|
97
|
-
*
|
|
142
|
+
* is no grant. A range mints `steps <list>`, exactly the steps it names in
|
|
143
|
+
* their normal form (`20, 5-9` mints `steps 5-9,20`), and beats `--auto`
|
|
144
|
+
* (bounded wins), so a range that does not parse mints nothing rather than
|
|
145
|
+
* falling back to `auto`. A bare `/build`, a bare step number, or any
|
|
146
|
+
* non-invocation prompt mints nothing. The namespaced `/plumbbob:build` form is
|
|
147
|
+
* honored alongside the bare one.
|
|
98
148
|
*
|
|
99
149
|
* The invocation must *start* a token: a slash preceded by a word character,
|
|
100
150
|
* `/`, `.`, `-`, or `~` is a path segment (`src/build`, `./build`), not a
|
|
@@ -114,43 +164,30 @@ export function grantFromPrompt(prompt) {
|
|
|
114
164
|
if (invocation === null)
|
|
115
165
|
return null;
|
|
116
166
|
const line = prompt.slice(invocation.index + invocation[0].length).split('\n', 1)[0] ?? '';
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (word === '')
|
|
122
|
-
continue;
|
|
123
|
-
if (word === '--auto') {
|
|
124
|
-
auto = true;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
const range = /^\d+-(\d+)$/.exec(word);
|
|
128
|
-
if (range !== null) {
|
|
129
|
-
ceiling = range[1];
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (/^\d+$/.test(word))
|
|
133
|
-
continue; // a bare step number: keep scanning for a range/--auto
|
|
134
|
-
break; // an unrecognized flag or free text: the arguments have ended
|
|
135
|
-
}
|
|
136
|
-
if (ceiling !== undefined)
|
|
137
|
-
return `range ${ceiling}`;
|
|
138
|
-
return auto ? 'auto' : null;
|
|
167
|
+
const args = readBuildArgs(line);
|
|
168
|
+
if (args.range)
|
|
169
|
+
return args.steps === null ? null : `steps ${normalizeStepList(args.steps)}`;
|
|
170
|
+
return args.auto ? 'auto' : null;
|
|
139
171
|
}
|
|
140
172
|
/**
|
|
141
|
-
*
|
|
173
|
+
* The `prompt` and `prompt_id` fields of the UserPromptSubmit JSON, as '' and
|
|
174
|
+
* null when either is absent.
|
|
142
175
|
*
|
|
143
176
|
* Absent, unparseable, or non-string input contributes nothing rather than
|
|
144
|
-
* wedging the tool: the tick still lands (a human turn did occur)
|
|
145
|
-
* prompt clears any GRANT; the verb never
|
|
177
|
+
* wedging the tool: the tick still lands (a human turn did occur), the empty
|
|
178
|
+
* prompt clears any GRANT, and a missing id records none; the verb never
|
|
179
|
+
* throws over bad stdin.
|
|
146
180
|
*/
|
|
147
|
-
function
|
|
181
|
+
function readPayload(raw) {
|
|
148
182
|
try {
|
|
149
183
|
const parsed = JSON.parse(raw);
|
|
150
|
-
return
|
|
184
|
+
return {
|
|
185
|
+
prompt: typeof parsed.prompt === 'string' ? parsed.prompt : '',
|
|
186
|
+
promptId: typeof parsed.prompt_id === 'string' && parsed.prompt_id !== '' ? parsed.prompt_id : null,
|
|
187
|
+
};
|
|
151
188
|
}
|
|
152
189
|
catch {
|
|
153
|
-
return '';
|
|
190
|
+
return { prompt: '', promptId: null };
|
|
154
191
|
}
|
|
155
192
|
}
|
|
156
193
|
/**
|
package/dist/verbs/use.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { existsSync } from 'node:fs';
|
|
12
12
|
import { findRepoRoot } from "../lib/git.js";
|
|
13
13
|
import { activeBuild, hasSession, intentPath, listBuilds, setActiveBuild, stepPath } from "../lib/sidecar.js";
|
|
14
|
-
import { advisory, ending, notice, transition } from "../lib/notice.js";
|
|
14
|
+
import { advisory, ending, notice, printEnding, transition } from "../lib/notice.js";
|
|
15
15
|
import { driverPointer } from "./handoff.js";
|
|
16
16
|
/**
|
|
17
17
|
* Switch the active-build cursor to the named build.
|
|
@@ -43,22 +43,26 @@ export function use(cwd, args) {
|
|
|
43
43
|
// The lead line, then the advisory it qualifies, then the pointer into the
|
|
44
44
|
// build just switched to: one fixed order for every ending, so the relay never
|
|
45
45
|
// has to guess which line leads.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
printEnding(root, {
|
|
47
|
+
tier: 'driver',
|
|
48
|
+
render: 'use',
|
|
49
|
+
text: ending({
|
|
50
|
+
lead: transition({
|
|
51
|
+
label: 'Active build',
|
|
52
|
+
fact: target,
|
|
53
|
+
detail: existsSync(stepPath(root, target)) ? ['a step is in flight'] : [],
|
|
54
|
+
}),
|
|
55
|
+
advisories: left === null
|
|
56
|
+
? []
|
|
57
|
+
: [
|
|
58
|
+
advisory({
|
|
59
|
+
fact: `build "${left}" has a step in flight`,
|
|
60
|
+
detail: ['its in-flight state is preserved'],
|
|
61
|
+
remedy: `plumbbob use ${left} to pick it back up`,
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
pointer: driverPointer(root, target),
|
|
51
65
|
}),
|
|
52
|
-
|
|
53
|
-
? []
|
|
54
|
-
: [
|
|
55
|
-
advisory({
|
|
56
|
-
fact: `build "${left}" has a step in flight`,
|
|
57
|
-
detail: ['its in-flight state is preserved'],
|
|
58
|
-
remedy: `plumbbob use ${left} to pick it back up`,
|
|
59
|
-
}),
|
|
60
|
-
],
|
|
61
|
-
pointer: driverPointer(root, target),
|
|
62
|
-
}));
|
|
66
|
+
});
|
|
63
67
|
return 0;
|
|
64
68
|
}
|
package/hooks/hooks.json
CHANGED
package/package.json
CHANGED
package/skills/build/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
|
|
4
|
-
argument-hint: "[step-number | step-range] [--auto]"
|
|
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`, or a list like `20,5-9`, self-approves exactly the steps it names, in build order, then stops."
|
|
4
|
+
argument-hint: "[step-number | step-range | step-list] [--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
|
---
|
|
@@ -35,9 +35,10 @@ switch with `/model` and rerun to honor it, or wave you on. Advisory, never a ga
|
|
|
35
35
|
## What this skill does, in order
|
|
36
36
|
|
|
37
37
|
1. **Pick the step.** Use the number you were invoked with (for example `/plumbbob:build 4`), or,
|
|
38
|
-
if you were given a range like `/plumbbob:build 1-3
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if you were given a range like `/plumbbob:build 1-3` or a list like `/plumbbob:build 20,5-9`,
|
|
39
|
+
start at its first undone step in build order, the one the dashboard above marks
|
|
40
|
+
`← requested`, and land only the steps it names (see the range note under `--auto`).
|
|
41
|
+
With no argument, don't resolve the next step yourself; the CLI does: bare `plumbbob build`
|
|
41
42
|
(step 2) enters the next undone step in build order (the `## Build order` line when the
|
|
42
43
|
plan has one, else the numbering) and refuses with a `/plumbbob:step` nudge when every
|
|
43
44
|
step is checkpointed.
|
|
@@ -144,6 +145,7 @@ switch with `/model` and rerun to honor it, or wave you on. Advisory, never a ga
|
|
|
144
145
|
paragraph when the step needs explaining>
|
|
145
146
|
|
|
146
147
|
## 1 <the first highlight: one sentence, one move>
|
|
148
|
+
|
|
147
149
|
<the full story: what moved, why, what was tried and dropped>
|
|
148
150
|
|
|
149
151
|
## 2 ...
|
|
@@ -180,32 +182,59 @@ switch with `/model` and rerun to honor it, or wave you on. Advisory, never a ga
|
|
|
180
182
|
line in the build-log's `## Log` and then truncates it, so the detail lands in the
|
|
181
183
|
tracked ledger rather than piling up on disk.
|
|
182
184
|
|
|
185
|
+
**A bare range's pause is its stop.** When the injected dashboard's ranges block lists
|
|
186
|
+
this step as a bare range (`/plumbbob:build 5`, its row marked `▸`), the plan reserved a
|
|
187
|
+
call for this pause, and the `stop:` line beneath the row names it. The recommendation
|
|
188
|
+
speaks to that call: the move you would take on the plan's question, and why, rather
|
|
189
|
+
than a verdict on the diff alone.
|
|
190
|
+
|
|
183
191
|
*Paste the turn.* Run `plumbbob handoff` and paste its output whole, verbatim, at top
|
|
184
192
|
level, trailing blank line included, then end the turn. Never nest it inside a fence of
|
|
185
|
-
your own; it carries fences of its own and they cannot nest. It prints the
|
|
186
|
-
its highlights, the Readout fence (its
|
|
187
|
-
rows folded with your three), a `diff`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
your own; it carries fences of its own and they cannot nest. It prints the Now line
|
|
194
|
+
naming the step in flight, the Summary and its highlights, the Readout fence (its
|
|
195
|
+
measured `check`, `seam`, `diff`, and `spent` rows folded with your three), a `diff`
|
|
196
|
+
fence when the change is 20 lines or fewer, the Verdict folded worst-of from the same
|
|
197
|
+
rows, Next Up, Your Call, and your recommendation last. Run it once: a second run at an
|
|
198
|
+
unchanged pause prints the one-line reminder instead of the card, so if that line comes
|
|
199
|
+
back where the card belongs (you ran handoff earlier in this turn), run
|
|
200
|
+
`plumbbob handoff --full` and paste that. The gate verdict's one home is that `check`
|
|
201
|
+
row: no standalone verdict line exists, and you never restate the verdict in prose. A
|
|
202
|
+
narrowed run names the slots it skipped there (`· without test`), and that named
|
|
203
|
+
narrowing is the whole disclosure. The notice checkride's Stop hook appends after your
|
|
204
|
+
turn is not yours to relay or repeat; the trailing blank line you kept is what lands it
|
|
205
|
+
on its own line. Relayed CLI strings keep their em-dashes; your own lines never use
|
|
206
|
+
one, the write-versus-relay line of
|
|
195
207
|
[D78 (em-dash-ban)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d78).
|
|
196
208
|
|
|
197
209
|
*Then read the reply as an ask or a direction.* A message that **asks** ("expand 2",
|
|
198
|
-
"what does that mean?", "why did the seam row flag that?") is an expand: answer it from
|
|
199
|
-
`git diff`, or from the build-log's `## Log` for an older step,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
210
|
+
"what does that mean?", "why did the seam row flag that?") is an expand: answer it from
|
|
211
|
+
the detail file, from `git diff`, or from the build-log's `## Log` for an older step,
|
|
212
|
+
never from recall, then run `plumbbob handoff` and paste its output beneath your answer,
|
|
213
|
+
whole and verbatim. The turn is your answer, then that output, and nothing after it. The
|
|
214
|
+
pause renders its card once
|
|
215
|
+
([D87 (pause-once)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d87)):
|
|
216
|
+
while nothing about it has changed, handoff prints one line,
|
|
217
|
+
`**Next Up**: Back to Step <N> of <M> - <title>`, in place of the card the human already
|
|
218
|
+
read, so your answer stays on screen. The CLI picks the form, never you: paste whichever
|
|
219
|
+
one it prints, and never retype the card or reach for `--full` to put it beneath an
|
|
220
|
+
answer.
|
|
221
|
+
|
|
222
|
+
Two moments bring the whole card back, and in both you run `plumbbob handoff --full` and
|
|
223
|
+
paste its output whole, with nothing written around it: the human asks to see the pause
|
|
224
|
+
(`show the pause`, or any wording of it), or the card is not in this conversation (a
|
|
225
|
+
fresh session, or one after `/clear`, that finds the step still at its pause). A
|
|
226
|
+
message that **directs** is needs-work: take it as what to change, and nothing lands
|
|
227
|
+
until the human says `looks good`. Once the change is made, rewrite the detail file and
|
|
228
|
+
end on `plumbbob handoff` as the first render did; the pause has changed, so it prints
|
|
229
|
+
the whole card.
|
|
204
230
|
|
|
205
231
|
**Then hand off with the next model.** `plumbbob checkpoint` prints the whole boundary
|
|
206
232
|
ending itself: `**Checkpoint**: Step N complete (<sha>)`, the Verdict the step earned,
|
|
207
|
-
any advisory (a staged path outside the seam is the common one),
|
|
208
|
-
|
|
233
|
+
any advisory (a staged path outside the seam is the common one), the Stop part when
|
|
234
|
+
this landing completes a range (`**Stop**: Range 3 of 5 landed - <its stop>`, the reason
|
|
235
|
+
the build stops here), and Next Up. Relay that block whole, the Stop part with the rest
|
|
236
|
+
(the relay check holds a boundary that dropped it), run no second command, and write
|
|
237
|
+
nothing around it. It is the
|
|
209
238
|
orientation tier (no Your Call block, no recommendation), and it points at the next
|
|
210
239
|
undone step carrying that step's `- model:` recommendation (the plan's
|
|
211
240
|
smallest-model-that-fits call) so the human knows which `/model` to select before
|
|
@@ -268,7 +297,18 @@ and approves in the human's place**, and it **chains**:
|
|
|
268
297
|
advance the loop), or a new decision is needed. The step is still in flight and the
|
|
269
298
|
human has a call to make about it, so end on the same turn the default pause does:
|
|
270
299
|
write the detail file, run `plumbbob handoff`, and paste its block.
|
|
271
|
-
- **A
|
|
300
|
+
- **A call the plan reserves halts at its pause.** A step the plan's `## Build ranges`
|
|
301
|
+
names as a bare range (`/plumbbob:build 10`) is a decision the plan left for the human,
|
|
302
|
+
and `plumbbob build` says so as it enters the step (`step 10 ends at its pause, where
|
|
303
|
+
the plan reserves the call for the human`). Build it, verify it, and end on the same
|
|
304
|
+
turn a halt on trouble does: write the detail file, run `plumbbob handoff`, and paste
|
|
305
|
+
its block. The latch would let it land, since `--auto` grants every step, so keeping
|
|
306
|
+
the step open is yours to do
|
|
307
|
+
([D94 (auto-and-stops)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d94)).
|
|
308
|
+
A range that lands its steps runs through, since its stop is a review that can wait
|
|
309
|
+
for the end of the run, and a typed range stays exactly as typed.
|
|
310
|
+
- **A clean halt lands its step first.** The top of a requested range is reached (the
|
|
311
|
+
next step in build order is one the range does not name, or none is left), or no
|
|
272
312
|
planned steps remain, and `plumbbob build` names that step as it enters it (for
|
|
273
313
|
example `step 3 is the last undone step in the plan`). That step checkpoints like
|
|
274
314
|
every step before it, and its
|
|
@@ -283,36 +323,54 @@ only because the human asked for it by name. A range hands the clock back once i
|
|
|
283
323
|
step has landed: the step after it waits for the human's next run. The default (no flag,
|
|
284
324
|
no range) always ends at the pause.
|
|
285
325
|
|
|
286
|
-
### A step range (`N-M`) is a bounded `--auto`
|
|
326
|
+
### A step range (`N-M`, or a list like `20,5-9`) is a bounded `--auto`
|
|
287
327
|
|
|
288
328
|
`/plumbbob:build 1-3` self-approves steps 1 through 3 exactly as `--auto` does, then **stops
|
|
289
|
-
once step 3 has landed** instead of chaining to done.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
329
|
+
once step 3 has landed** instead of chaining to done. A range may also list its steps, numbers
|
|
330
|
+
and spans joined by commas (`/plumbbob:build 20,5-9`, typed with or without a space after a
|
|
331
|
+
comma), and it grants exactly the steps it names: the checkpoint refuses any other
|
|
332
|
+
([D91 (step-list-grants)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d91)).
|
|
333
|
+
The range *is* the opt-in; you do not also pass `--auto`.
|
|
334
|
+
|
|
335
|
+
Run it as the `--auto` loop, walked in build order. **Start at the range's first undone step
|
|
336
|
+
in build order, however it is written** (`5-9,20` under an order that puts 20 first starts at
|
|
337
|
+
20), and after each landing take the next undone step in build order. Build it if the range
|
|
338
|
+
names it; **stop before building any step the range does not name**, or once none is left. It
|
|
339
|
+
adds no machinery: just the one more entry already in the halt list above, and it is a clean
|
|
340
|
+
halt, so the top step is never held back for approval. The human approved it by typing the
|
|
341
|
+
range, and `plumbbob build` says so as it enters that step:
|
|
342
|
+
`step 3 is the top of the range you granted`, or, when the walk ends early (the plan runs
|
|
343
|
+
out, or the next step in build order is one the range does not name),
|
|
296
344
|
`the last undone step in the range you granted`.
|
|
297
345
|
|
|
298
|
-
-
|
|
299
|
-
|
|
300
|
-
and lands the single step `N`. Only a
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
check. Do not skip past
|
|
309
|
-
|
|
310
|
-
|
|
346
|
+
- **A range that parses**: build each step it names in build order, self-approving and
|
|
347
|
+
checkpointing each, the last included, then relay the last one's checkpoint block and end
|
|
348
|
+
the turn. `N-N` is a range like any other: it builds and lands the single step `N`. Only a
|
|
349
|
+
bare `/plumbbob:build N` ends at the pause.
|
|
350
|
+
- **A range that does not parse** (for example `3-1`, or `1--3`): that is not a range you can
|
|
351
|
+
walk, and it grants nothing; report it and stop rather than guess the intent.
|
|
352
|
+
- **A number past the last planned step** (for example `1-9` with three steps): build through
|
|
353
|
+
the last planned step the range names and stop; that is the existing "no planned steps
|
|
354
|
+
remain" halt, not an error. A number the plan lacks is never reached.
|
|
355
|
+
- **A step inside the range won't build** (its seam won't parse): `plumbbob build <n>` fails
|
|
356
|
+
exactly as it always does; surface it and stop, the same as a red check. Do not skip past
|
|
357
|
+
the gap.
|
|
358
|
+
- **The range starts above the next undone step**: you are jumping over earlier planned work;
|
|
359
|
+
note that you are skipping the steps before it, then proceed (the same latitude as a
|
|
311
360
|
single-number jump like `/plumbbob:build 4`).
|
|
312
|
-
- **The build order puts a
|
|
313
|
-
`1, 4, 2, 3` under `/plumbbob:build 1-3`):
|
|
314
|
-
|
|
315
|
-
|
|
361
|
+
- **The build order puts a step the range does not name inside it** (the order reads
|
|
362
|
+
`1, 4, 2, 3` under `/plumbbob:build 1-3`): the halt above fires before step 4, and
|
|
363
|
+
`plumbbob build` names step 1 as the last undone step in the range. The range that walks
|
|
364
|
+
the whole stretch lists step 4 too (`/plumbbob:build 1-4`, or `/plumbbob:build 1,4,2,3`).
|
|
365
|
+
- **At a range's stop, harvest before the next range.** No harvest runs between the steps
|
|
366
|
+
inside a range, so the parks pile up, and a blocker among them would otherwise ride into
|
|
367
|
+
the next one. When the human asks what comes next at a stop with parks open (the note's
|
|
368
|
+
`parked 2 of 7 open`), the move is `/plumbbob:harvest`, then the range the card names.
|
|
369
|
+
- **After an early stop, settle the step, then rerun what is left.** A halt on trouble
|
|
370
|
+
inside a range ends at that step's pause, and the grant ends with the turn. Once the
|
|
371
|
+
human approves and the step lands, the note beneath Next Up names what is left of the
|
|
372
|
+
range (`· range 3 of 5: /plumbbob:build 8-9`), and that is the command the human types
|
|
373
|
+
to carry on.
|
|
316
374
|
|
|
317
375
|
## The hard contracts
|
|
318
376
|
|
package/skills/harvest/SKILL.md
CHANGED
|
@@ -23,6 +23,10 @@ DESIGN boundary, not mid-step. Read the dashboard injected above:
|
|
|
23
23
|
Chasing parked items mid-step is the disease parking prevents.
|
|
24
24
|
- At the boundary (the dashboard reads `[DESIGN]`): go ahead.
|
|
25
25
|
|
|
26
|
+
A range's stop is the boundary that matters most. No harvest runs between the steps
|
|
27
|
+
inside a range, so the parks it piled up wait for the stop where it lands, and a
|
|
28
|
+
blocker among them is caught there before the next range builds on it.
|
|
29
|
+
|
|
26
30
|
## What this skill does
|
|
27
31
|
|
|
28
32
|
Walk the **Park list** in `build-log.md` item by item and, for each, **propose exactly
|
|
@@ -47,6 +51,11 @@ one line of reasoning, then **wait for the human to confirm or override**. Write
|
|
|
47
51
|
(`- <a id="d5"></a>**D5 (retry-cap)**: <the call>, *because* <why>`), never a bare `D5`.
|
|
48
52
|
A blocker that becomes a new step takes the next free number under `## Steps` and,
|
|
49
53
|
when it has to land before an existing undone step, its place in the sequence through
|
|
50
|
-
`plumbbob order 7 8 5` (the `## Build order` line), never by renumbering.
|
|
54
|
+
`plumbbob order 7 8 5` (the `## Build order` line), never by renumbering. When the plan
|
|
55
|
+
carries `## Build ranges`, the new step goes into a range too (the one it builds with,
|
|
56
|
+
or a bare range of its own when it brings a call the human has to make before it
|
|
57
|
+
lands), and a range it breaks is re-cut in the same confirmed edit
|
|
58
|
+
([D92 (build-ranges)](https://github.com/robmclarty/plumbbob/blob/main/docs/decisions.md#d92));
|
|
59
|
+
`plumbbob status` afterward carries a `⚠` row for any range still broken.
|
|
51
60
|
- Never reclassify or resolve an item the human hasn't confirmed, and default every
|
|
52
61
|
uncertain item to **tangent**, never to blocker.
|