plumbbob 0.9.5 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +87 -86
- package/dist/cli-core.js +8 -8
- package/dist/cli.js +1 -1
- package/dist/lib/agents.js +26 -26
- package/dist/lib/buildlog.js +11 -11
- package/dist/lib/buildlogsync.js +8 -8
- package/dist/lib/check.js +14 -14
- package/dist/lib/commitbody.js +72 -0
- package/dist/lib/commitmsg.js +7 -7
- package/dist/lib/git.js +44 -15
- package/dist/lib/intent.js +20 -19
- package/dist/lib/latch.js +16 -16
- package/dist/lib/orient.js +36 -17
- package/dist/lib/reentry.js +3 -3
- package/dist/lib/settings.js +16 -16
- package/dist/lib/sidecar.js +54 -54
- package/dist/lib/templates.js +1 -1
- package/dist/verbs/agent.js +25 -25
- package/dist/verbs/build.js +9 -9
- package/dist/verbs/check.js +6 -6
- package/dist/verbs/checkpoint.js +48 -47
- package/dist/verbs/doctor.js +27 -26
- package/dist/verbs/finish.js +27 -40
- package/dist/verbs/handoff.js +9 -8
- package/dist/verbs/init.js +9 -9
- package/dist/verbs/park.js +3 -3
- package/dist/verbs/recover.js +11 -11
- package/dist/verbs/revert.js +12 -12
- package/dist/verbs/spike.js +17 -17
- package/dist/verbs/start.js +49 -19
- package/dist/verbs/status.js +8 -8
- package/dist/verbs/turn.js +16 -16
- package/dist/verbs/use.js +4 -4
- package/node_modules/checkride/.claude-plugin/plugin.json +1 -1
- package/node_modules/checkride/CHANGELOG.md +401 -0
- package/node_modules/checkride/README.md +27 -9
- package/node_modules/checkride/dist/adapters.d.ts +12 -4
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +21 -1
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts +9 -1
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/cursor.js +59 -1
- package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/harness.d.ts +1 -1
- package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/harness.js +10 -1
- package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts +23 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.js +387 -82
- package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.js +2 -2
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +14 -5
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +58 -5
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
- package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/history.js +232 -0
- package/node_modules/checkride/dist/baseline/history.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +4 -2
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +2 -1
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +30 -7
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +50 -16
- package/node_modules/checkride/dist/baseline/store.js.map +1 -1
- package/node_modules/checkride/dist/cli.d.ts +5 -1
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +98 -25
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +33 -4
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +21 -6
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.js +1 -1
- package/node_modules/checkride/dist/doctor.d.ts +2 -0
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +39 -1
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +49 -4
- package/node_modules/checkride/dist/gate.d.ts.map +1 -1
- package/node_modules/checkride/dist/gate.js +143 -16
- package/node_modules/checkride/dist/gate.js.map +1 -1
- package/node_modules/checkride/dist/init.d.ts +8 -1
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +68 -7
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/node-pin.d.ts +1 -1
- package/node_modules/checkride/dist/node-pin.js +1 -1
- package/node_modules/checkride/dist/orchestrator.d.ts +13 -0
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +118 -13
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pm/index.d.ts +1 -0
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/index.js.map +1 -1
- package/node_modules/checkride/dist/pm/launch.d.ts +23 -2
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/launch.js +16 -0
- package/node_modules/checkride/dist/pm/launch.js.map +1 -1
- package/node_modules/checkride/dist/pm/translate.js +1 -1
- package/node_modules/checkride/dist/pm/translate.js.map +1 -1
- package/node_modules/checkride/dist/qa/mutation.d.ts +4 -4
- package/node_modules/checkride/dist/qa/mutation.js +4 -4
- package/node_modules/checkride/dist/recover.d.ts +48 -0
- package/node_modules/checkride/dist/recover.d.ts.map +1 -0
- package/node_modules/checkride/dist/recover.js +220 -0
- package/node_modules/checkride/dist/recover.js.map +1 -0
- package/node_modules/checkride/dist/smoke.d.ts +1 -1
- package/node_modules/checkride/dist/smoke.js +1 -1
- package/node_modules/checkride/dist/snippets.d.ts +1 -1
- package/node_modules/checkride/package.json +3 -2
- package/node_modules/checkride/schema/checkride.config.schema.json +9 -1
- package/node_modules/checkride/skills/check/SKILL.md +9 -1
- package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
- package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
- package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
- package/node_modules/checkride/templates/shared/vale.ini +34 -0
- package/package.json +10 -9
- package/skills/build/SKILL.md +69 -64
- package/skills/doctor/SKILL.md +11 -11
- package/skills/finish/SKILL.md +20 -19
- package/skills/harvest/SKILL.md +13 -12
- package/skills/park/SKILL.md +10 -10
- package/skills/plan/SKILL.md +63 -54
- package/skills/recover/SKILL.md +6 -6
- package/skills/refine/SKILL.md +18 -16
- package/skills/revert/SKILL.md +6 -6
- package/skills/spike/SKILL.md +9 -9
- package/skills/status/SKILL.md +5 -5
- package/skills/step/SKILL.md +17 -12
- package/skills/verify/SKILL.md +45 -40
- package/templates/build-log.md +7 -7
- package/templates/intent.md +49 -39
- package/templates/spike-report.md +10 -10
package/dist/lib/latch.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
// The approval latch
|
|
1
|
+
// The approval latch: the ledger-plane predicate `checkpoint` evaluates before
|
|
2
2
|
// its check gate (the latch is cheap, so it runs first; the heavy gate already
|
|
3
|
-
// ran in the verify tick). The work plane stays guidance
|
|
4
|
-
// blocked
|
|
3
|
+
// ran in the verify tick). The work plane stays guidance (no edit is ever
|
|
4
|
+
// blocked) but the commit tick is latched: a step may not land unless a human
|
|
5
5
|
// turn intervened since the step was entered, the human granted a one-turn
|
|
6
6
|
// self-approval from their literal prompt, or the turn ledger is dormant (a
|
|
7
7
|
// host with no hooks grows no ledger, and the latch stays out of the way rather
|
|
8
8
|
// than wedging). The ledger is a pair of flat sidecar control files: TURN
|
|
9
9
|
// counts human prompts (ticked by the `plumbbob turn` hook), TICK stamps that
|
|
10
|
-
// count when a step is entered
|
|
10
|
+
// count when a step is entered; TURN > TICK means a human turn intervened.
|
|
11
11
|
// Five rows, evaluated in order, first hit wins; the refusal message *is* the
|
|
12
12
|
// pause affordance. A standing `auto` in a settings file is never a grant: a
|
|
13
|
-
// model can write that file, and a grant it can forge is no grant
|
|
13
|
+
// model can write that file, and a grant it can forge is no grant; the latch
|
|
14
14
|
// reads it only to say so at the pause, never to allow. It reads only
|
|
15
|
-
// TURN/TICK/GRANT and isTTY
|
|
15
|
+
// TURN/TICK/GRANT and isTTY; it never sniffs the host session. Named exports,
|
|
16
16
|
// node builtins.
|
|
17
17
|
import { readFileSync } from 'node:fs';
|
|
18
18
|
import { grantPath, tickPath, turnPath } from "./sidecar.js";
|
|
19
19
|
import { resolveBoolean } from "./settings.js";
|
|
20
20
|
const ALLOW = { allow: true };
|
|
21
21
|
/**
|
|
22
|
-
* The refusal message is the affordance
|
|
22
|
+
* The refusal message is the affordance; it tells the model what the pause is,
|
|
23
23
|
* how it ends (approval on the next turn lands it; `/plumbbob:build` only starts the
|
|
24
24
|
* next step), and names the only legitimate grants.
|
|
25
25
|
*/
|
|
@@ -31,7 +31,7 @@ boundary; \`/plumbbob:build\` only starts the next step, it never lands this one
|
|
|
31
31
|
`;
|
|
32
32
|
/**
|
|
33
33
|
* The plan commit gets its own wording: there is no step, no diff, and no
|
|
34
|
-
* self-review at plan time
|
|
34
|
+
* self-review at plan time; what the human approves is the plan itself.
|
|
35
35
|
*/
|
|
36
36
|
const NO_TURN_PLAN_MESSAGE = `plumbbob: checkpoint refused — no human turn since \`start\` stamped this plan. This
|
|
37
37
|
is the plan pause: present the plan, then end the turn; the human's approving
|
|
@@ -39,7 +39,7 @@ message is the tick that lets it land on the next run. (An explicit \`/plumbbob:
|
|
|
39
39
|
step range in the human's own prompt is the only self-approval.)
|
|
40
40
|
`;
|
|
41
41
|
/**
|
|
42
|
-
* A settings `auto` is never a grant
|
|
42
|
+
* A settings `auto` is never a grant; a model can write that file, and a grant
|
|
43
43
|
* it can forge is no grant. When one is set, the refusal names it rather than
|
|
44
44
|
* silently ignoring it, and points at the only self-approval that works.
|
|
45
45
|
*/
|
|
@@ -60,19 +60,19 @@ function ceilingMessage(ceiling) {
|
|
|
60
60
|
*/
|
|
61
61
|
export function evaluateLatch(input) {
|
|
62
62
|
if (input.isTTY)
|
|
63
|
-
return ALLOW; // 1
|
|
63
|
+
return ALLOW; // 1: a human at the keyboard
|
|
64
64
|
if (input.turn === null || input.tick === null)
|
|
65
|
-
return ALLOW; // 2
|
|
65
|
+
return ALLOW; // 2: dormant ledger / hand-built diff
|
|
66
66
|
if (input.grant?.kind === 'auto')
|
|
67
|
-
return ALLOW; // 3
|
|
67
|
+
return ALLOW; // 3: one-turn grant the human typed
|
|
68
68
|
if (input.grant?.kind === 'range' && input.step !== null) {
|
|
69
69
|
if (input.step <= input.grant.ceiling)
|
|
70
70
|
return ALLOW;
|
|
71
71
|
return { allow: false, reason: 'ceiling', message: ceilingMessage(input.grant.ceiling) };
|
|
72
72
|
}
|
|
73
73
|
if (input.turn > input.tick)
|
|
74
|
-
return ALLOW; // 4
|
|
75
|
-
// 5
|
|
74
|
+
return ALLOW; // 4: a human turn intervened
|
|
75
|
+
// 5: the pause (plan-worded when no step is landing). A settings `auto` never
|
|
76
76
|
// allows; when one is set, the refusal notes it rather than honoring it.
|
|
77
77
|
const base = input.step === null ? NO_TURN_PLAN_MESSAGE : NO_TURN_MESSAGE;
|
|
78
78
|
return { allow: false, reason: 'no-turn', message: input.settingsAuto ? base + SETTINGS_AUTO_NOTE : base };
|
|
@@ -80,7 +80,7 @@ export function evaluateLatch(input) {
|
|
|
80
80
|
/**
|
|
81
81
|
* Parse a GRANT file's content (`auto` | `range <M>`).
|
|
82
82
|
*
|
|
83
|
-
* Anything else
|
|
83
|
+
* Anything else (including a corrupted or hand-mangled file) contributes
|
|
84
84
|
* nothing: no grant, never an error.
|
|
85
85
|
*/
|
|
86
86
|
export function parseGrant(raw) {
|
|
@@ -105,7 +105,7 @@ export function checkLatch(root, step) {
|
|
|
105
105
|
tick: readCount(tickPath(root)),
|
|
106
106
|
grant: readGrant(root),
|
|
107
107
|
step,
|
|
108
|
-
// Read from the settings ladder
|
|
108
|
+
// Read from the settings ladder, but only to decorate the pause, never to allow.
|
|
109
109
|
settingsAuto: resolveBoolean(root, 'auto', false),
|
|
110
110
|
});
|
|
111
111
|
}
|
package/dist/lib/orient.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The orientation model behind `status`: parse a live build session into the
|
|
2
|
-
// where-am-I dashboard
|
|
2
|
+
// where-am-I dashboard: title, phase, step list, last checkpoint, counts, and
|
|
3
3
|
// one suggested next move. Pure and best-effort by design: it takes raw file
|
|
4
4
|
// contents (no fs), and a malformed document degrades to fewer fields rather
|
|
5
5
|
// than throwing.
|
|
@@ -20,7 +20,7 @@ function sectionLines(content, heading) {
|
|
|
20
20
|
return lines.slice(start + 1, end);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* The build title
|
|
23
|
+
* The build title: the first `# ` heading in intent.md, or null when absent.
|
|
24
24
|
*/
|
|
25
25
|
export function parseTitle(intent) {
|
|
26
26
|
for (const line of intent.split('\n')) {
|
|
@@ -32,12 +32,14 @@ export function parseTitle(intent) {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* Parse the steps under `## Steps`: `N. [ |x] Title
|
|
35
|
+
* Parse the steps under `## Steps`: `N. [ |x] Title, **done when:** ...`
|
|
36
|
+
* (the legacy em-dash separator before the marker parses the same).
|
|
36
37
|
*
|
|
37
|
-
* The title is the text
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* The title is the text before the `**done when:**` marker on the opener line,
|
|
39
|
+
* trailing separator stripped; an opener without the marker falls back to the
|
|
40
|
+
* text up to the first em dash. `planned` is true when the step's block carries
|
|
41
|
+
* a `done when` criterion. Only `## Steps` is machine-read as the build plan;
|
|
42
|
+
* narrative roadmap prose lives in its own section and never lands here.
|
|
41
43
|
*/
|
|
42
44
|
export function parseSteps(intent) {
|
|
43
45
|
const lines = sectionLines(intent, '## Steps');
|
|
@@ -46,10 +48,12 @@ export function parseSteps(intent) {
|
|
|
46
48
|
const m = /^(\d+)\.\s+\[([ xX])\]\s+(.*)$/.exec(line);
|
|
47
49
|
if (m) {
|
|
48
50
|
const rest = m[3] ?? '';
|
|
51
|
+
const marker = /\*\*done when:\*\*/i.exec(rest);
|
|
52
|
+
const head = marker ? rest.slice(0, marker.index) : (rest.split('—')[0] ?? rest);
|
|
49
53
|
starts.push({
|
|
50
54
|
n: Number(m[1]),
|
|
51
55
|
done: (m[2] ?? ' ').toLowerCase() === 'x',
|
|
52
|
-
title:
|
|
56
|
+
title: head.replace(/\s*[—,-]+\s*$/, '').trim(),
|
|
53
57
|
idx,
|
|
54
58
|
});
|
|
55
59
|
}
|
|
@@ -88,13 +92,28 @@ export function markStepDone(intent, n) {
|
|
|
88
92
|
}
|
|
89
93
|
/**
|
|
90
94
|
* Count the questions still open: `- Q\d+:` openers under `## Open questions`
|
|
91
|
-
* that
|
|
95
|
+
* that carry neither a resolution marker nor an unfilled scaffold body.
|
|
92
96
|
*
|
|
93
|
-
* The opener may carry a slug-at-birth gloss
|
|
94
|
-
*
|
|
97
|
+
* The opener may carry a slug-at-birth gloss (`- Q2 (some-slug): ...`) and the
|
|
98
|
+
* anchored form a citable question is born in:
|
|
99
|
+
* `- <a id="q2"></a>**Q2 (some-slug)**: ...`, which is what a `[Q2 (some-slug)](#q2)`
|
|
100
|
+
* reference site elsewhere in the file lands on. The count reads through both, and
|
|
101
|
+
* through any mix of them a hand-edited intent leaves mid-build; sub-lines
|
|
102
|
+
* (`*plain:*`/`*lean:*`) never match.
|
|
103
|
+
*
|
|
104
|
+
* Two rules settle the rest. "Resolved" counts as a marker only as a whole word, so
|
|
105
|
+
* a question whose opener still calls itself "unresolved" keeps counting; a bare
|
|
106
|
+
* substring read that as resolved and dropped a live hole off the dashboard. And an
|
|
107
|
+
* opener whose body has not been filled in yet, still opening on its `<...>`
|
|
108
|
+
* fill-in, is scaffold rather than a question, which is what makes a fresh build
|
|
109
|
+
* report zero rather than one.
|
|
95
110
|
*/
|
|
96
111
|
export function parseOpenQuestions(intent) {
|
|
97
|
-
|
|
112
|
+
const opener = /^-\s+(?:<a id="[^"]*"><\/a>\s*)?\*{0,2}Q\d+(?: \([^)]+\))?\*{0,2}:\s*(.*)$/;
|
|
113
|
+
return sectionLines(intent, '## Open questions').filter((l) => {
|
|
114
|
+
const body = opener.exec(l.trim())?.[1];
|
|
115
|
+
return body !== undefined && !/\bresolved\b/i.test(l) && !/^<[^>]*>/.test(body);
|
|
116
|
+
}).length;
|
|
98
117
|
}
|
|
99
118
|
/**
|
|
100
119
|
* Count the open parked items: `- [ ]` lines under `## Park list`.
|
|
@@ -122,12 +141,12 @@ export function parseLastCheckpoint(checkpoints) {
|
|
|
122
141
|
return last;
|
|
123
142
|
}
|
|
124
143
|
/**
|
|
125
|
-
* The SHA of the last ledger line of ANY kind
|
|
144
|
+
* The SHA of the last ledger line of ANY kind: baseline, plan, or step.
|
|
126
145
|
*
|
|
127
146
|
* This is the reconciliation anchor for the out-of-band commit count: a commit
|
|
128
147
|
* that lands after the plan but before the first step checkpoint is exactly the
|
|
129
148
|
* window the reconciliation line exists for, so anchoring on step lines alone
|
|
130
|
-
* would leave it invisible. `parseLastCheckpoint` stays step-only
|
|
149
|
+
* would leave it invisible. `parseLastCheckpoint` stays step-only; it feeds
|
|
131
150
|
* the "last checkpoint step N" display, where baseline/plan would be noise.
|
|
132
151
|
*/
|
|
133
152
|
export function lastLedgerSha(checkpoints) {
|
|
@@ -162,7 +181,7 @@ function nextMove(spiking, steps, inFlight, parked) {
|
|
|
162
181
|
return 'plan the first step — `/plumbbob:step`';
|
|
163
182
|
}
|
|
164
183
|
// Batch-default: the steps were planned up front, so finishing them usually
|
|
165
|
-
// means "finish up"
|
|
184
|
+
// means "finish up", but `/plumbbob:step` can still add an increment if reality grew.
|
|
166
185
|
const harvest = parked > 0 ? `harvest ${parked} parked idea${parked === 1 ? '' : 's'} — \`/plumbbob:harvest\`; then ` : '';
|
|
167
186
|
return `${harvest}finish up — \`/plumbbob:finish\` (or \`/plumbbob:step\` to add another increment)`;
|
|
168
187
|
}
|
|
@@ -207,7 +226,7 @@ export function formatOrientation(o) {
|
|
|
207
226
|
return head;
|
|
208
227
|
}
|
|
209
228
|
// Surface the next step's detail so the human can review it (and `/plumbbob:step`-
|
|
210
|
-
// revise) before building. Only what's present
|
|
229
|
+
// revise) before building. Only what's present: a rough step shows neither.
|
|
211
230
|
const detail = [];
|
|
212
231
|
if (o.nextDoneWhen !== null) {
|
|
213
232
|
detail.push(` done when: ${o.nextDoneWhen}`);
|
|
@@ -225,7 +244,7 @@ export function formatOrientation(o) {
|
|
|
225
244
|
const cpLine = cp === null ? 'last checkpoint none yet' : `last checkpoint step ${cp.n} · ${cp.sha.slice(0, 7)}`;
|
|
226
245
|
// A neutral reconciliation note, only when there is something to reconcile:
|
|
227
246
|
// commits landed since the last checkpoint that plumbbob's ledger didn't
|
|
228
|
-
// record. Informational
|
|
247
|
+
// record. Informational: the human commits freely, so this never gates.
|
|
229
248
|
const receipts = o.outOfBand > 0
|
|
230
249
|
? [`${o.outOfBand} commit${o.outOfBand === 1 ? '' : 's'} since the last checkpoint landed outside plumbbob's ledger.`]
|
|
231
250
|
: [];
|
package/dist/lib/reentry.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
// The failure it exists to stop is not slowness, it is unbounded recursion. A
|
|
5
5
|
// repo whose `check` setting is its own full pipeline (`pnpm check`) has a gate
|
|
6
|
-
// that runs its test suite; anything in that suite which gates THAT SAME REPO
|
|
7
|
-
// a test driving `checkpoint` against the process cwd
|
|
6
|
+
// that runs its test suite; anything in that suite which gates THAT SAME REPO
|
|
7
|
+
// (a test driving `checkpoint` against the process cwd) spawns a second full
|
|
8
8
|
// pipeline, whose suite spawns a third. Each generation forks wider than the
|
|
9
9
|
// last, and nothing in the loop is aware there is a loop. The only thing that
|
|
10
10
|
// ended the first observed occurrence was checkride's 600s per-check cap
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// It is scoped BY ROOT, and that is the whole design. A flat "a gate is
|
|
14
14
|
// running" flag is wrong in the one case that matters most: plumbbob's own
|
|
15
15
|
// suite legitimately gates dozens of fixture repos, and when the suite itself
|
|
16
|
-
// is running under this repo's gate it inherits the marker
|
|
16
|
+
// is running under this repo's gate it inherits the marker, so a flat flag
|
|
17
17
|
// refuses every one of them and turns a green suite red. Recursion is a repo
|
|
18
18
|
// re-entering ITS OWN gate; a different root is ordinary nested work and must
|
|
19
19
|
// pass through untouched.
|
package/dist/lib/settings.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
// The settings ladder: a resolved setting comes from, in priority order,
|
|
2
|
-
// 1. a CLI flag
|
|
3
|
-
// 2. settings.local.json
|
|
4
|
-
// 3. settings.json
|
|
5
|
-
// 4. a built-in default
|
|
2
|
+
// 1. a CLI flag: passed in by the verb (undefined when absent)
|
|
3
|
+
// 2. settings.local.json: untracked personal overlay, per-worktree
|
|
4
|
+
// 3. settings.json: tracked project defaults
|
|
5
|
+
// 4. a built-in default: supplied by the caller
|
|
6
6
|
// The first defined rung wins. Both files are optional JSON; a missing or
|
|
7
7
|
// malformed file contributes nothing rather than throwing, so a broken personal
|
|
8
8
|
// overlay can never wedge the tool. Functional and procedural, node builtins only.
|
|
9
9
|
//
|
|
10
|
-
// Known keys: `check` (string
|
|
11
|
-
// default in settings.json) and `auto` (boolean
|
|
10
|
+
// Known keys: `check` (string: the command for the heavy verify gate, a project
|
|
11
|
+
// default in settings.json) and `auto` (boolean: a personal preference, so it
|
|
12
12
|
// belongs in settings.local.json). `auto` is NOT a checkpoint grant: the approval
|
|
13
|
-
// latch never reads it to allow a land
|
|
14
|
-
// it can forge is no grant
|
|
13
|
+
// latch never reads it to allow a land (a model can write this file, and a grant
|
|
14
|
+
// it can forge is no grant) only to name it at the pause when set; self-approval
|
|
15
15
|
// comes solely from the human's literal `/build --auto` or a typed step range.
|
|
16
|
-
// The per-worktree active-build cursor is NOT here
|
|
16
|
+
// The per-worktree active-build cursor is NOT here: it lives in the STATE file
|
|
17
17
|
// (see sidecar.ts), which keeps this overlay a human-authored file rather than
|
|
18
18
|
// tool-managed state.
|
|
19
19
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
@@ -34,7 +34,7 @@ export function localSettingsPath(root) {
|
|
|
34
34
|
/**
|
|
35
35
|
* Read one settings file as a plain object.
|
|
36
36
|
*
|
|
37
|
-
* A missing, malformed, or non-object file yields {}
|
|
37
|
+
* A missing, malformed, or non-object file yields {}: an empty rung that
|
|
38
38
|
* contributes nothing rather than wedging the tool.
|
|
39
39
|
*/
|
|
40
40
|
function readSettings(path) {
|
|
@@ -61,7 +61,7 @@ function resolveSetting(root, key, flag) {
|
|
|
61
61
|
return readSettings(settingsPath(root))[key];
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* Resolve a string setting (
|
|
64
|
+
* Resolve a string setting (for example `check`).
|
|
65
65
|
*
|
|
66
66
|
* A missing rung, or one holding a non-string / blank value, yields the
|
|
67
67
|
* caller's fallback rather than gating on garbage.
|
|
@@ -71,7 +71,7 @@ export function resolveString(root, key, fallback, flag) {
|
|
|
71
71
|
return typeof value === 'string' && value.trim().length > 0 ? value : fallback;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
|
-
* Resolve a boolean setting (
|
|
74
|
+
* Resolve a boolean setting (for example `auto`). A missing or non-boolean rung
|
|
75
75
|
* yields the caller's fallback.
|
|
76
76
|
*/
|
|
77
77
|
export function resolveBoolean(root, key, fallback, flag) {
|
|
@@ -79,10 +79,10 @@ export function resolveBoolean(root, key, fallback, flag) {
|
|
|
79
79
|
return typeof value === 'boolean' ? value : fallback;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Resolve a non-negative integer setting (
|
|
82
|
+
* Resolve a non-negative integer setting (for example `agentTimeout`).
|
|
83
83
|
*
|
|
84
84
|
* A missing rung, or one holding a non-finite / negative / non-integer number,
|
|
85
|
-
* yields the caller's fallback rather than a garbage timeout
|
|
85
|
+
* yields the caller's fallback rather than a garbage timeout: 0 means "no
|
|
86
86
|
* timeout", so a broken value must never silently become one.
|
|
87
87
|
*/
|
|
88
88
|
export function resolveNumber(root, key, fallback, flag) {
|
|
@@ -90,11 +90,11 @@ export function resolveNumber(root, key, fallback, flag) {
|
|
|
90
90
|
return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
* Resolve an object-valued setting (
|
|
93
|
+
* Resolve an object-valued setting (for example the `agents` slot-binding defaults)
|
|
94
94
|
* across the same ladder.
|
|
95
95
|
*
|
|
96
96
|
* The first defined, object-typed rung wins; a missing rung, or one holding a
|
|
97
|
-
* non-object / array / scalar, yields {} rather than garbage
|
|
97
|
+
* non-object / array / scalar, yields {} rather than garbage: mirrors the
|
|
98
98
|
* string/boolean/number resolvers above. No flag rung: the caller (`agent
|
|
99
99
|
* run`) merges these under the per-build harness bindings, not over a flag.
|
|
100
100
|
*/
|