plumbbob 0.8.11 → 0.8.12
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/dist/lib/commitmsg.js +72 -0
- package/dist/lib/sidecar.js +12 -0
- package/dist/verbs/checkpoint.js +22 -21
- package/dist/verbs/finish.js +12 -17
- package/package.json +1 -1
- package/skills/pb-finish/SKILL.md +3 -2
- package/skills/pb-plan/SKILL.md +9 -1
- package/skills/pb-step/SKILL.md +5 -2
- package/skills/pb-verify/SKILL.md +5 -4
- package/templates/intent.md +6 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plumbbob",
|
|
3
3
|
"displayName": "PlumbBob",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.12",
|
|
5
5
|
"description": "Attention-first build process — driver skills + a CLI that keep you the decider: guidance on the work, a latch on the record.",
|
|
6
6
|
"author": { "name": "Rob McLarty", "email": "hello@robmclarty.com", "url": "https://robmclarty.com" },
|
|
7
7
|
"homepage": "https://github.com/robmclarty/plumbbob#readme",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Conventional-Commit subject assembly (D68). Plumbbob's commit subjects read as
|
|
2
|
+
// `type(scope): description` — real feat/fix/chore types and a real parenthesised
|
|
3
|
+
// scope — so `git log` speaks the same grammar as the rest of the branch. The
|
|
4
|
+
// `plumbbob`/`step N` identifiers that used to prefix the subject (the old D34
|
|
5
|
+
// shape) now ride a marker line at the head of the body, which `withMarker`
|
|
6
|
+
// prepends — so `git log --grep plumbbob` still finds every plumbbob commit.
|
|
7
|
+
// Functional/procedural, node-builtins-only (C1/C2), no gate: these only shape a
|
|
8
|
+
// string the CLI is about to commit.
|
|
9
|
+
// The Conventional-Commit type vocabulary plumbbob recognises as a leading prefix
|
|
10
|
+
// on a step title. Anchoring on this closed set keeps a prose title like
|
|
11
|
+
// `Note: rewire the cache` from being misread as a `Note` type — only these words,
|
|
12
|
+
// immediately followed by an optional `(scope)`, an optional `!`, and a colon,
|
|
13
|
+
// count as an author-supplied prefix.
|
|
14
|
+
const TYPES = ['feat', 'fix', 'chore', 'docs', 'refactor', 'test', 'perf', 'build', 'ci', 'style', 'revert'];
|
|
15
|
+
const PREFIX = new RegExp(`^(${TYPES.join('|')})(?:\\(([^)]+)\\))?(!)?:\\s+(.+)$`);
|
|
16
|
+
// Split an optional Conventional prefix off a step title. `feat(escape-hatch): add
|
|
17
|
+
// the flag` → { type:'feat', scope:'escape-hatch', breaking:false, description:'add
|
|
18
|
+
// the flag' }. A title with no recognised prefix comes back whole as the
|
|
19
|
+
// description with a null type/scope — the caller then defaults the type and fills
|
|
20
|
+
// the scope from the build.
|
|
21
|
+
export function parseConventionalTitle(title) {
|
|
22
|
+
const m = PREFIX.exec(title.trim());
|
|
23
|
+
if (m === null) {
|
|
24
|
+
return { type: null, scope: null, breaking: false, description: title.trim() };
|
|
25
|
+
}
|
|
26
|
+
return { type: m[1] ?? null, scope: m[2] ?? null, breaking: m[3] === '!', description: (m[4] ?? '').trim() };
|
|
27
|
+
}
|
|
28
|
+
// Assemble a `type(scope)!: description` subject, omitting the `(scope)` segment
|
|
29
|
+
// when there is no scope (Conventional Commits allow a bare `type: description`)
|
|
30
|
+
// and the `!` when the change is not breaking.
|
|
31
|
+
export function conventionalSubject(parts) {
|
|
32
|
+
const scope = parts.scope ? `(${parts.scope})` : '';
|
|
33
|
+
const bang = parts.breaking ? '!' : '';
|
|
34
|
+
return `${parts.type}${scope}${bang}: ${parts.description}`;
|
|
35
|
+
}
|
|
36
|
+
// A Conventional subject from a step title, filling gaps from the build. An
|
|
37
|
+
// author-written prefix (`fix(parser): …`) is honoured verbatim — its type, scope,
|
|
38
|
+
// breaking marker, and exact wording all win. A bare prose title defaults to
|
|
39
|
+
// `defaultType` and the build `scope`, and gets its sentence-case opener
|
|
40
|
+
// de-capitalised (`Add the flag` → `add the flag`) to match Conventional style —
|
|
41
|
+
// while an all-caps acronym start (`API`, `OAuth`) is left alone.
|
|
42
|
+
export function subjectFromTitle(title, defaultType, scope) {
|
|
43
|
+
const parsed = parseConventionalTitle(title);
|
|
44
|
+
if (parsed.type !== null) {
|
|
45
|
+
return conventionalSubject({
|
|
46
|
+
type: parsed.type,
|
|
47
|
+
scope: parsed.scope ?? scope,
|
|
48
|
+
breaking: parsed.breaking,
|
|
49
|
+
description: parsed.description,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return conventionalSubject({ type: defaultType, scope, description: lowerFirst(parsed.description) });
|
|
53
|
+
}
|
|
54
|
+
// Prepend a marker line (`plumbbob step 1`, `plumbbob plan`, `plumbbob finish`) to
|
|
55
|
+
// an optional body, separated by a blank line so git keeps them as distinct
|
|
56
|
+
// paragraphs. Always returns at least the marker, so every plumbbob commit carries
|
|
57
|
+
// the greppable identifier in its body even when the prose is empty.
|
|
58
|
+
export function withMarker(marker, body) {
|
|
59
|
+
const rest = body?.trim();
|
|
60
|
+
return rest !== undefined && rest.length > 0 ? `${marker}\n\n${rest}` : marker;
|
|
61
|
+
}
|
|
62
|
+
// De-capitalise a sentence-case opener but leave an acronym/proper all-caps start
|
|
63
|
+
// (`API`, `OAuth`) intact: lowercase the first character only when it is uppercase
|
|
64
|
+
// and the second character is not (so `Add` → `add`, but `API` stays `API`).
|
|
65
|
+
function lowerFirst(text) {
|
|
66
|
+
const a = text[0] ?? '';
|
|
67
|
+
const b = text[1] ?? '';
|
|
68
|
+
if (a >= 'A' && a <= 'Z' && b >= 'A' && b <= 'Z') {
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
return a >= 'A' && a <= 'Z' ? a.toLowerCase() + text.slice(1) : text;
|
|
72
|
+
}
|
package/dist/lib/sidecar.js
CHANGED
|
@@ -40,6 +40,18 @@ export function slugify(title) {
|
|
|
40
40
|
.replace(/[^a-z0-9]+/g, '-')
|
|
41
41
|
.replace(/^-+|-+$/g, '');
|
|
42
42
|
}
|
|
43
|
+
// The Conventional-Commit scope for a build (D68): its slug with the `YYYY-MM-DD-`
|
|
44
|
+
// date prefix `start` prepends stripped off, so `2026-07-18-escape-hatch` yields the
|
|
45
|
+
// human-meaningful `escape-hatch`. Null when no build resolves (`--local`, or a slug
|
|
46
|
+
// that is nothing but a date) — the caller then omits the `(scope)` segment, which
|
|
47
|
+
// Conventional Commits permit.
|
|
48
|
+
export function buildScope(slug) {
|
|
49
|
+
if (slug === null) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const stripped = slug.replace(/^\d{4}-\d{2}-\d{2}-/, '');
|
|
53
|
+
return stripped.length > 0 ? stripped : null;
|
|
54
|
+
}
|
|
43
55
|
// Existing build slugs — the directory names under `builds/`, sorted. Empty when
|
|
44
56
|
// `builds/` is absent (a `--local` repo, or before the first tracked `start`).
|
|
45
57
|
export function listBuilds(root) {
|
package/dist/verbs/checkpoint.js
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
// the same way.
|
|
10
10
|
import { appendFileSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { commit, findRepoRoot, headSha, isDirty, stageAll, stagePath, stagedPaths, stagedStat } from "../lib/git.js";
|
|
12
|
-
import { buildFolder, buildLogPath, bumpStepStat, checkpointsPath, clearHandoff, clearTick, hasSession, intentPath, readStats, seamPath, stampStepStat, stepPath, } from "../lib/sidecar.js";
|
|
12
|
+
import { activeBuild, buildFolder, buildLogPath, buildScope, bumpStepStat, checkpointsPath, clearHandoff, clearTick, hasSession, intentPath, readStats, seamPath, stampStepStat, stepPath, } from "../lib/sidecar.js";
|
|
13
13
|
import { runCheck } from "../lib/check.js";
|
|
14
14
|
import { checkLatch } from "../lib/latch.js";
|
|
15
|
-
import { markStepDone, parseSteps
|
|
15
|
+
import { markStepDone, parseSteps } from "../lib/orient.js";
|
|
16
16
|
import { parseStepSeam, scopeDrift } from "../lib/intent.js";
|
|
17
|
+
import { conventionalSubject, subjectFromTitle, withMarker } from "../lib/commitmsg.js";
|
|
17
18
|
import { appendToSection, checkpointLogLine } from "../lib/buildlog.js";
|
|
18
19
|
export async function checkpoint(cwd, args) {
|
|
19
20
|
const root = findRepoRoot(cwd);
|
|
@@ -53,7 +54,7 @@ export async function checkpoint(cwd, args) {
|
|
|
53
54
|
if (isDirty(root)) {
|
|
54
55
|
stageAll(root);
|
|
55
56
|
warnScopeDrift(root, step);
|
|
56
|
-
const body = bodyArg(args) ?? fallbackBody(root, step);
|
|
57
|
+
const body = withMarker(`plumbbob step ${step}`, bodyArg(args) ?? fallbackBody(root, step));
|
|
57
58
|
sha = commit(root, messageArg(args) ?? subjectForStep(root, step), body);
|
|
58
59
|
}
|
|
59
60
|
else {
|
|
@@ -71,12 +72,12 @@ export async function checkpoint(cwd, args) {
|
|
|
71
72
|
return 0;
|
|
72
73
|
}
|
|
73
74
|
// The plan-approval commit (D36): stage only the active build's artifact folder and
|
|
74
|
-
// commit it as `
|
|
75
|
+
// commit it as `chore(<scope>): plan` (D68), then record a `plan <sha>` line. Giving
|
|
75
76
|
// the plan its own commit keeps the first step's diff from absorbing the scaffold, so
|
|
76
77
|
// `git log` reads baseline → plan → steps. No check gate (there is no code work to
|
|
77
78
|
// verify yet), no intent flip, no step markers — the plan lives entirely in DESIGN.
|
|
78
|
-
// An optional `--body` (stdin heredoc,
|
|
79
|
-
// artifact plane, so there is no scope-drift to warn about.
|
|
79
|
+
// An optional `--body` (stdin heredoc, D68) rides after the `plumbbob plan` marker;
|
|
80
|
+
// the folder is whitelisted artifact plane, so there is no scope-drift to warn about.
|
|
80
81
|
function checkpointPlan(root, args) {
|
|
81
82
|
// The latch (D64) covers the plan commit too, keyed on the TICK that `start`
|
|
82
83
|
// stamped: the plan pause is a pause. No step number — a range grant does not
|
|
@@ -87,7 +88,7 @@ function checkpointPlan(root, args) {
|
|
|
87
88
|
return 1;
|
|
88
89
|
}
|
|
89
90
|
stagePath(root, buildFolder(root));
|
|
90
|
-
const sha = commit(root, planSubject(root), bodyArg(args) ?? undefined);
|
|
91
|
+
const sha = commit(root, planSubject(root), withMarker('plumbbob plan', bodyArg(args) ?? undefined));
|
|
91
92
|
appendFileSync(checkpointsPath(root), `plan ${sha}\n`);
|
|
92
93
|
// The plan landing consumes `start`'s entry stamp (D64): a later hand-built diff
|
|
93
94
|
// (no `build <n>`) must find no stale TICK and stay guidance-governed.
|
|
@@ -95,17 +96,11 @@ function checkpointPlan(root, args) {
|
|
|
95
96
|
process.stdout.write(`plumbbob: plan committed — ${sha.slice(0, 9)}. Baseline → plan → steps.\n`);
|
|
96
97
|
return 0;
|
|
97
98
|
}
|
|
98
|
-
// The plan commit's CLI-owned subject:
|
|
99
|
-
//
|
|
99
|
+
// The plan commit's CLI-owned subject (D68): `chore(<scope>): plan`, the scope drawn
|
|
100
|
+
// from the active build's slug (a bare `chore: plan` when none resolves). The
|
|
101
|
+
// `plumbbob plan` identifier rides the body marker, not the subject.
|
|
100
102
|
function planSubject(root) {
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
title = parseTitle(readFileSync(intentPath(root), 'utf8'));
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
title = null;
|
|
107
|
-
}
|
|
108
|
-
return title ? `plumbbob: plan — ${title}` : 'plumbbob: plan';
|
|
103
|
+
return conventionalSubject({ type: 'chore', scope: buildScope(activeBuild(root)), description: 'plan' });
|
|
109
104
|
}
|
|
110
105
|
// Step resolution (D3): explicit arg > in-flight STEP file > first undone step in
|
|
111
106
|
// intent.md. Returns null when none can be determined. A `-m` value is a message,
|
|
@@ -209,12 +204,18 @@ function statsSuffix(root, step) {
|
|
|
209
204
|
}
|
|
210
205
|
return parts.length > 0 ? parts.join(', ') : null;
|
|
211
206
|
}
|
|
212
|
-
// The CLI-owned, deterministic commit subject:
|
|
213
|
-
//
|
|
214
|
-
//
|
|
207
|
+
// The CLI-owned, deterministic commit subject (D68): a Conventional `type(scope):
|
|
208
|
+
// description`. The scope comes from the active build's slug; the type and wording
|
|
209
|
+
// come from the step's title — an author-written prefix (`fix(parser): …`) is honoured
|
|
210
|
+
// verbatim, a bare title defaults to `feat`. A titleless step falls back to
|
|
211
|
+
// `chore(<scope>): checkpoint`. The `plumbbob`/`step N` identifiers ride the body
|
|
212
|
+
// marker, not the subject; a `-m` override or `--body` prose is a separate concern.
|
|
215
213
|
function subjectForStep(root, step) {
|
|
214
|
+
const scope = buildScope(activeBuild(root));
|
|
216
215
|
const title = titleForStep(root, step);
|
|
217
|
-
return title
|
|
216
|
+
return title !== null && title.trim().length > 0
|
|
217
|
+
? subjectFromTitle(title, 'feat', scope)
|
|
218
|
+
: conventionalSubject({ type: 'chore', scope, description: 'checkpoint' });
|
|
218
219
|
}
|
|
219
220
|
function titleForStep(root, step) {
|
|
220
221
|
try {
|
package/dist/verbs/finish.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
// `plumbbob finish` (D9/
|
|
1
|
+
// `plumbbob finish` (D9/D68) — the close-out: append the checkpoint SHAs to the
|
|
2
2
|
// report, make the final commit, and clear the control state. The build folder is
|
|
3
3
|
// NOT deleted — it IS the archive now (D29): tracked, it merges with the branch and
|
|
4
4
|
// shows up in the PR, so nothing is copied into a local-only `archive/` (that
|
|
5
5
|
// helper retired with this step). No refuse-without-report gate — guidance offers
|
|
6
6
|
// the artifact, it does not wall the exit (D9). Git footprint stays additive (C5):
|
|
7
|
-
// one forward commit under the
|
|
7
|
+
// one forward commit under the Conventional `chore(<scope>): finish` subject, its
|
|
8
|
+
// `plumbbob finish` identifier riding the body marker (D68).
|
|
8
9
|
import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs';
|
|
9
10
|
import { join } from 'node:path';
|
|
10
11
|
import { commit, findRepoRoot, isDirty, stageAll } from "../lib/git.js";
|
|
11
|
-
import { checkpointsPath, clearTick, hasSession,
|
|
12
|
-
import {
|
|
12
|
+
import { buildScope, checkpointsPath, clearTick, hasSession, readStats, reportPath, resolveBuild, seamPath, setGrant, sidecarDir, spikePath, stepPath, } from "../lib/sidecar.js";
|
|
13
|
+
import { conventionalSubject, withMarker } from "../lib/commitmsg.js";
|
|
13
14
|
export function finish(cwd, args = []) {
|
|
14
15
|
const root = findRepoRoot(cwd);
|
|
15
16
|
if (root === null || !hasSession(root)) {
|
|
@@ -33,7 +34,7 @@ export function finish(cwd, args = []) {
|
|
|
33
34
|
if (isDirty(root)) {
|
|
34
35
|
stageAll(root);
|
|
35
36
|
}
|
|
36
|
-
const sha = commit(root, subject(
|
|
37
|
+
const sha = commit(root, subject(slug), withMarker('plumbbob finish', bodyArg(args) ?? undefined));
|
|
37
38
|
// Clear the control state: the in-flight markers first, then the session sentinel
|
|
38
39
|
// last (so "no session" flips exactly at the end). Deleting STATE also drops the
|
|
39
40
|
// cursor (D28) — cursor and session share the one file now, so a single delete does
|
|
@@ -53,18 +54,12 @@ export function finish(cwd, args = []) {
|
|
|
53
54
|
'Run `/pb-plan` (or `plumbbob start "<title>"`) to frame the next goal.\n');
|
|
54
55
|
return 0;
|
|
55
56
|
}
|
|
56
|
-
// The CLI-owned final-commit subject (
|
|
57
|
-
// the
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
title = parseTitle(readFileSync(intentPath(root, slug), 'utf8'));
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
title = null;
|
|
66
|
-
}
|
|
67
|
-
return title ? `plumbbob: finish — ${title}` : 'plumbbob: finish';
|
|
57
|
+
// The CLI-owned final-commit subject (D68): `chore(<scope>): finish`, the scope drawn
|
|
58
|
+
// from the build's slug (a bare `chore: finish` when none resolves, e.g. `--local`),
|
|
59
|
+
// mirroring the plan/step Conventional shape so one grammar spans the whole history.
|
|
60
|
+
// The `plumbbob finish` identifier rides the body marker, not the subject.
|
|
61
|
+
function subject(slug) {
|
|
62
|
+
return conventionalSubject({ type: 'chore', scope: buildScope(slug), description: 'finish' });
|
|
68
63
|
}
|
|
69
64
|
// `--body` reads the final-commit body from stdin (the single-quoted heredoc of
|
|
70
65
|
// D34), so the pb-finish skill can compose a proportional close-out message. Returns
|
package/package.json
CHANGED
|
@@ -29,8 +29,9 @@ record.
|
|
|
29
29
|
- **Deferred tangents** — the harvested items that become future work.
|
|
30
30
|
This is the "yeah, I did that" artifact. Write it by default; the human may edit it.
|
|
31
31
|
2. **Finish.** Run `plumbbob finish`, which appends the checkpoint SHAs to the
|
|
32
|
-
report and makes the final commit — subject `
|
|
33
|
-
|
|
32
|
+
report and makes the final commit — subject `chore(<scope>): finish`, its body
|
|
33
|
+
led by a `plumbbob finish` marker plus an optional proportional `--body` (the
|
|
34
|
+
stdin heredoc) — then
|
|
34
35
|
clears the control state (the in-flight markers, then `STATE` last — one delete
|
|
35
36
|
drops both the session sentinel and the active-build cursor it carries). The
|
|
36
37
|
build folder stays in place, committed, and rides the branch into the PR.
|
package/skills/pb-plan/SKILL.md
CHANGED
|
@@ -74,6 +74,13 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
|
|
|
74
74
|
sharpened just-in-time when you reach them with `/pb-step`. Keep each small enough to
|
|
75
75
|
verify in one review pass.
|
|
76
76
|
|
|
77
|
+
**Give the title a Conventional-Commit type where the change has one** *(optional)*:
|
|
78
|
+
a `<title>` that opens with `fix:`, `feat(escape-hatch):`, `chore:`, … becomes the
|
|
79
|
+
checkpoint's commit subject verbatim (its scope and breaking `!` honored) — this is
|
|
80
|
+
how each step lands a *real* `feat`/`fix`/`chore` in `git log`. Skip the prefix and
|
|
81
|
+
the checkpoint defaults the type to `feat` and draws the scope from the build slug, so
|
|
82
|
+
a plain title still yields a clean `feat(<slug>): <title>` ([D68](../../docs/decisions.md#d68)).
|
|
83
|
+
|
|
77
84
|
**Recommend a model per step where the signal is clear** *(optional)*: the
|
|
78
85
|
`- model:` sub-line names the **smallest model that can carry the step**, with the
|
|
79
86
|
one-phrase why — the human buys capability only where the step needs it. E.g.
|
|
@@ -106,7 +113,8 @@ an agent can follow with `/pb-build`. The argument only seeds how you get there.
|
|
|
106
113
|
up automatically (it lives in the build folder).
|
|
107
114
|
6. **Commit the plan.** Once the human approves the frame and steps, run
|
|
108
115
|
`plumbbob checkpoint --plan` to commit the scaffold on its own — subject
|
|
109
|
-
`
|
|
116
|
+
`chore(<scope>): plan` (with a `plumbbob plan` body marker), only `.plumbbob/builds/<slug>/`,
|
|
117
|
+
a `plan <sha>` line in
|
|
110
118
|
`checkpoints`. This keeps the first step's diff clean, so history reads
|
|
111
119
|
baseline → plan → steps. Pass a proportional `--body` (the single-quoted stdin
|
|
112
120
|
heredoc) when the rationale is worth carrying; skip it for a small plan. Do this
|
package/skills/pb-step/SKILL.md
CHANGED
|
@@ -39,8 +39,11 @@ grew — but its everyday job is to sharpen, not to invent.
|
|
|
39
39
|
a step that sharpened into rote work can drop to a smaller model; one that grew subtle
|
|
40
40
|
earns a frontier one. Advisory, plain text (no backticks), never a gate.
|
|
41
41
|
3. **Get the human's OK**, then write it into `## Steps` in the standard format —
|
|
42
|
-
`N. [ ] <title> — **done when:** <criterion>` with a `- seam:` sub-line.
|
|
43
|
-
|
|
42
|
+
`N. [ ] <title> — **done when:** <criterion>` with a `- seam:` sub-line. Give the
|
|
43
|
+
`<title>` an optional Conventional-Commit prefix (`fix:`, `feat(scope):`, …) when the
|
|
44
|
+
change has a clear type — it becomes the checkpoint's commit subject; a plain title
|
|
45
|
+
defaults to `feat` with the build slug as scope ([D68](../../docs/decisions.md#d68)).
|
|
46
|
+
Revise the existing step in place; only append when you are genuinely adding an increment.
|
|
44
47
|
4. **Revise the step's harness bindings if they drifted too** *(optional)*. If the
|
|
45
48
|
build carries a `harness.json` (beside `intent.md`) and the reality that moved the
|
|
46
49
|
step also changed which agents it wants, sharpen that step's slot bindings
|
|
@@ -47,10 +47,11 @@ this skill verifies it the same way: **it reads the diff, not the author**.
|
|
|
47
47
|
the human is the clock. Never checkpoint without it.
|
|
48
48
|
6. **Checkpoint** *(only after approval)*. Run `plumbbob checkpoint`: it makes the WIP
|
|
49
49
|
commit, records the SHA, flips the step to done, appends the step to the build-log's
|
|
50
|
-
`## Log`, and returns to DESIGN. The CLI owns the commit **subject**
|
|
51
|
-
(
|
|
52
|
-
the
|
|
53
|
-
|
|
50
|
+
`## Log`, and returns to DESIGN. The CLI owns the commit **subject** — a Conventional
|
|
51
|
+
`<type>(<scope>): <description>` composed from the step title and the build slug — and
|
|
52
|
+
leads the **body** with a `plumbbob step N` marker; you own the rest of the **body**.
|
|
53
|
+
Compose a body *proportional to the step* — a one-liner for a trivial change, a short
|
|
54
|
+
paragraph on the what/why for a meatier one — and pass it on stdin:
|
|
54
55
|
|
|
55
56
|
```bash
|
|
56
57
|
plumbbob checkpoint <n> --body <<'BODY'
|
package/templates/intent.md
CHANGED
|
@@ -50,11 +50,13 @@ which `/pb-build` records in the build folder's `SEAM` for orientation — aware
|
|
|
50
50
|
An optional **model** line recommends the smallest model that can carry the step —
|
|
51
51
|
mechanical work runs fine on a small model; subtle or creative work earns a frontier
|
|
52
52
|
one. Advisory for the human, never a gate; write it plain, no backticks (D62).
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
A step title may open with a Conventional-Commit type (`fix:`, `feat(scope):`) — it
|
|
54
|
+
becomes the checkpoint's commit subject; skip it and the subject defaults to `feat`,
|
|
55
|
+
scoped by the build slug (D68). Then drive `/pb-build` until done. Later steps may be
|
|
56
|
+
fuzzier than the first; sharpen the next one just-in-time with `/pb-step` (empty input
|
|
57
|
+
auto-syncs it), and use `/pb-refine` to repair the whole plan when a blocker rewrites it.)*
|
|
56
58
|
|
|
57
|
-
1. [ ] <step> — **done when:** <criterion, ideally a test or check result>
|
|
59
|
+
1. [ ] feat: <step> — **done when:** <criterion, ideally a test or check result>
|
|
58
60
|
- seam: `<file>`, `<file>`
|
|
59
61
|
- model: <smallest that can carry it, e.g. sonnet — mechanical, fully specified>
|
|
60
62
|
2. [ ] <step> — **done when:** <criterion>
|