continuous-improvement 3.22.1 → 3.24.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/marketplace.json +1 -1
- package/CHANGELOG.md +23 -0
- package/QUICKSTART.md +19 -20
- package/README.md +72 -19
- package/SKILL.md +4 -0
- package/bin/companion-preference-status.mjs +2 -5
- package/bin/generate-plugin-manifests.mjs +21 -2
- package/bin/harvest-friction.mjs +10 -8
- package/bin/install.mjs +208 -33
- package/bin/mcp-server.mjs +4 -9
- package/bin/observe.mjs +3 -3
- package/bin/reconcile-instinct-hashes.mjs +226 -0
- package/commands/discipline.md +5 -2
- package/commands/reconcile.md +1 -1
- package/commands/ship.md +5 -49
- package/commands/superpowers.md +1 -1
- package/commands/verify-install.md +8 -3
- package/hooks/companion-preference.mjs +2 -5
- package/hooks/config-guard.mjs +94 -0
- package/hooks/gateguard.mjs +39 -39
- package/hooks/goal-drift-stop.mjs +2 -2
- package/hooks/query-cost-nudge.mjs +2 -2
- package/hooks/recall-briefing.mjs +2 -2
- package/hooks/route-prompt.mjs +2 -5
- package/hooks/session.mjs +2 -2
- package/hooks/workflow-distill.mjs +2 -2
- package/lib/config-guard-gate.mjs +243 -0
- package/lib/destructive-bash.mjs +216 -0
- package/lib/gateguard-state.mjs +5 -1
- package/lib/plugin-metadata.mjs +12 -1
- package/lib/skill-catalog.mjs +169 -0
- package/llms.txt +11 -0
- package/package.json +1 -1
- package/plugins/beginner.json +2 -2
- package/plugins/continuous-improvement/.claude-plugin/marketplace.json +1 -1
- package/plugins/continuous-improvement/.claude-plugin/plugin.json +1 -1
- package/plugins/continuous-improvement/README.md +1 -2
- package/plugins/continuous-improvement/bin/mcp-server.mjs +4 -9
- package/plugins/continuous-improvement/bin/observe.mjs +3 -3
- package/plugins/continuous-improvement/commands/discipline.md +5 -2
- package/plugins/continuous-improvement/commands/reconcile.md +1 -1
- package/plugins/continuous-improvement/commands/ship.md +5 -49
- package/plugins/continuous-improvement/commands/superpowers.md +1 -1
- package/plugins/continuous-improvement/commands/verify-install.md +8 -3
- package/plugins/continuous-improvement/hooks/companion-preference.mjs +2 -5
- package/plugins/continuous-improvement/hooks/config-guard.mjs +94 -0
- package/plugins/continuous-improvement/hooks/gateguard.mjs +39 -39
- package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +2 -2
- package/plugins/continuous-improvement/hooks/hooks.json +10 -0
- package/plugins/continuous-improvement/hooks/query-cost-nudge.mjs +2 -2
- package/plugins/continuous-improvement/hooks/recall-briefing.mjs +2 -2
- package/plugins/continuous-improvement/hooks/route-prompt.mjs +2 -5
- package/plugins/continuous-improvement/hooks/session.mjs +2 -2
- package/plugins/continuous-improvement/hooks/workflow-distill.mjs +2 -2
- package/plugins/continuous-improvement/lib/config-guard-gate.mjs +243 -0
- package/plugins/continuous-improvement/lib/destructive-bash.mjs +216 -0
- package/plugins/continuous-improvement/lib/gateguard-state.mjs +5 -1
- package/plugins/continuous-improvement/lib/plugin-metadata.mjs +12 -1
- package/plugins/continuous-improvement/skills/README.md +1 -1
- package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +4 -0
- package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +1 -1
- package/plugins/continuous-improvement/skills/gateguard/SKILL.md +17 -2
- package/plugins/continuous-improvement/skills/reconcile/SKILL.md +0 -1
- package/plugins/continuous-improvement/skills/ship/SKILL.md +139 -0
- package/plugins/expert.json +1 -1
- package/skills/README.md +2 -2
- package/skills/deploy-receipt.md +1 -1
- package/skills/gateguard.md +17 -2
- package/skills/reconcile.md +0 -1
- package/skills/ship.md +139 -0
- package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +0 -77
- package/skills/safety-guard.md +0 -77
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destructive-Bash classifier for hooks/gateguard.mjs.
|
|
3
|
+
*
|
|
4
|
+
* Two layers, pure and dependency-free so a table test and a future
|
|
5
|
+
* `gateguard-explain` CLI can drive it without spawning the hook:
|
|
6
|
+
*
|
|
7
|
+
* 1. Structured rules: the command is split at unquoted separators
|
|
8
|
+
* (`|`, `||`, `&&`, `;`, newline) and each simple command is tokenized,
|
|
9
|
+
* so flag order and spelling stop mattering. `rm -r -f`, `rm -Rf` and
|
|
10
|
+
* `rm --recursive --force` are the same command; `git clean -fdx` and
|
|
11
|
+
* `git clean --force -d` are the same command. Each rule has a stable id
|
|
12
|
+
* the deny reason prints, so a block is explainable.
|
|
13
|
+
* 2. The original substring list, kept verbatim as the fallback so nothing
|
|
14
|
+
* that was caught before stops being caught.
|
|
15
|
+
*
|
|
16
|
+
* What is deliberately NOT here: plain file writes through Bash (`cat > x`,
|
|
17
|
+
* `sed -i`). Gating those would break the operator workflow that uses Bash
|
|
18
|
+
* precisely to write files; that limit is disclosed on the site and README.
|
|
19
|
+
*
|
|
20
|
+
* Message-flag carve-out: the VALUE of a commit message, PR body, title, or
|
|
21
|
+
* note is prose, never a command. It is blanked before any rule runs, so
|
|
22
|
+
* `git commit -m "rm -rf the old helper"` stays allowed. `-c` is not a message
|
|
23
|
+
* flag: `bash -c "rm -rf /"` carries a real command and still gates.
|
|
24
|
+
*/
|
|
25
|
+
export const DESTRUCTIVE_PATTERNS = [
|
|
26
|
+
"rm -rf",
|
|
27
|
+
"rm -fr",
|
|
28
|
+
"git reset --hard",
|
|
29
|
+
"git push --force",
|
|
30
|
+
"git push -f",
|
|
31
|
+
"--force-with-lease",
|
|
32
|
+
"git branch -D",
|
|
33
|
+
"drop table",
|
|
34
|
+
"drop database",
|
|
35
|
+
"drop schema",
|
|
36
|
+
"truncate ",
|
|
37
|
+
"mkfs",
|
|
38
|
+
"dd if=",
|
|
39
|
+
"format ",
|
|
40
|
+
"rmdir /s",
|
|
41
|
+
"del /f /q",
|
|
42
|
+
"del /q /f",
|
|
43
|
+
"Remove-Item -Recurse",
|
|
44
|
+
"Remove-Item -Force",
|
|
45
|
+
];
|
|
46
|
+
// Flags whose VALUE is human prose (a commit message, a PR body) or a filename —
|
|
47
|
+
// never a command to execute. Their contents must not trip the destructive scan.
|
|
48
|
+
const MESSAGE_FLAG_RE = /(^|\s)(-m|--message|-F|--file|--body|--body-file|--title|--notes|-C|--reuse-message)(=|\s+)('[^']*'|"[^"]*"|\S+)/g;
|
|
49
|
+
/** Blank the value of every message/body flag; the flag itself is preserved. */
|
|
50
|
+
export function stripMessageArgs(command) {
|
|
51
|
+
return command.replace(MESSAGE_FLAG_RE, (_match, lead, flag) => `${lead}${flag} `);
|
|
52
|
+
}
|
|
53
|
+
// Split at shell separators that sit outside single or double quotes.
|
|
54
|
+
function splitSimpleCommands(command) {
|
|
55
|
+
const segments = [];
|
|
56
|
+
let current = "";
|
|
57
|
+
let quote = null;
|
|
58
|
+
for (let i = 0; i < command.length; i++) {
|
|
59
|
+
const ch = command[i];
|
|
60
|
+
if (quote) {
|
|
61
|
+
current += ch;
|
|
62
|
+
if (ch === quote)
|
|
63
|
+
quote = null;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (ch === '"' || ch === "'") {
|
|
67
|
+
quote = ch;
|
|
68
|
+
current += ch;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (ch === "\n" || ch === ";") {
|
|
72
|
+
segments.push(current);
|
|
73
|
+
current = "";
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (ch === "|" || ch === "&") {
|
|
77
|
+
// `||`, `&&`, `|`, `&` all end the simple command; collapse a doubled char.
|
|
78
|
+
if (command[i + 1] === ch)
|
|
79
|
+
i++;
|
|
80
|
+
segments.push(current);
|
|
81
|
+
current = "";
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
current += ch;
|
|
85
|
+
}
|
|
86
|
+
segments.push(current);
|
|
87
|
+
return segments.map((s) => s.trim()).filter((s) => s !== "");
|
|
88
|
+
}
|
|
89
|
+
function tokenize(segment) {
|
|
90
|
+
return segment.split(/\s+/).filter((t) => t !== "");
|
|
91
|
+
}
|
|
92
|
+
// Drop `sudo`, `env`, and leading VAR=value assignments so the command word is
|
|
93
|
+
// the first token the rules look at.
|
|
94
|
+
function commandTokens(segment) {
|
|
95
|
+
const tokens = tokenize(segment);
|
|
96
|
+
let i = 0;
|
|
97
|
+
while (i < tokens.length) {
|
|
98
|
+
const t = tokens[i];
|
|
99
|
+
if (t === "sudo" || t === "env" || t === "command" || /^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) {
|
|
100
|
+
i++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
return tokens.slice(i);
|
|
106
|
+
}
|
|
107
|
+
// For `git`, skip global options (`-C <dir>`, `-c k=v`, `--git-dir=…`) to find
|
|
108
|
+
// the subcommand and its own arguments.
|
|
109
|
+
function gitSubcommand(tokens) {
|
|
110
|
+
if (tokens[0] !== "git")
|
|
111
|
+
return null;
|
|
112
|
+
let i = 1;
|
|
113
|
+
while (i < tokens.length && tokens[i].startsWith("-")) {
|
|
114
|
+
const t = tokens[i];
|
|
115
|
+
i += t === "-C" || t === "-c" || t === "--git-dir" || t === "--work-tree" ? 2 : 1;
|
|
116
|
+
}
|
|
117
|
+
const sub = tokens[i];
|
|
118
|
+
if (!sub)
|
|
119
|
+
return null;
|
|
120
|
+
return { sub, args: tokens.slice(i + 1) };
|
|
121
|
+
}
|
|
122
|
+
const isShortFlag = (t) => /^-[A-Za-z]+$/.test(t);
|
|
123
|
+
const hasShortLetter = (t, letters) => isShortFlag(t) && letters.test(t.slice(1));
|
|
124
|
+
const STRUCTURED_RULES = [
|
|
125
|
+
{
|
|
126
|
+
id: "rm-recursive-force",
|
|
127
|
+
test: (tokens) => {
|
|
128
|
+
if (tokens[0] !== "rm")
|
|
129
|
+
return false;
|
|
130
|
+
const flags = tokens.slice(1).filter((t) => t.startsWith("-"));
|
|
131
|
+
const recursive = flags.some((t) => t === "--recursive" || hasShortLetter(t, /[rR]/));
|
|
132
|
+
const force = flags.some((t) => t === "--force" || hasShortLetter(t, /f/));
|
|
133
|
+
return recursive && force;
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "git-clean-force",
|
|
138
|
+
test: (tokens) => {
|
|
139
|
+
const git = gitSubcommand(tokens);
|
|
140
|
+
if (!git || git.sub !== "clean")
|
|
141
|
+
return false;
|
|
142
|
+
const dryRun = git.args.some((t) => t === "--dry-run" || hasShortLetter(t, /n/));
|
|
143
|
+
const force = git.args.some((t) => t === "--force" || hasShortLetter(t, /f/));
|
|
144
|
+
return force && !dryRun;
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "git-checkout-discard",
|
|
149
|
+
test: (tokens) => {
|
|
150
|
+
const git = gitSubcommand(tokens);
|
|
151
|
+
if (!git || git.sub !== "checkout")
|
|
152
|
+
return false;
|
|
153
|
+
const dash = git.args.indexOf("--");
|
|
154
|
+
if (dash !== -1)
|
|
155
|
+
return git.args.length > dash + 1; // `git checkout -- <pathspec>` discards
|
|
156
|
+
const first = git.args.find((t) => !t.startsWith("-"));
|
|
157
|
+
return first === "." || first === "./";
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "git-restore-discard",
|
|
162
|
+
test: (tokens) => {
|
|
163
|
+
const git = gitSubcommand(tokens);
|
|
164
|
+
if (!git || git.sub !== "restore")
|
|
165
|
+
return false;
|
|
166
|
+
const pathspec = git.args.some((t) => !t.startsWith("-"));
|
|
167
|
+
if (!pathspec)
|
|
168
|
+
return false;
|
|
169
|
+
const staged = git.args.some((t) => t === "--staged" || hasShortLetter(t, /S/));
|
|
170
|
+
const worktree = git.args.some((t) => t === "--worktree" || hasShortLetter(t, /W/));
|
|
171
|
+
return !staged || worktree; // `--staged` alone only unstages
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "find-delete",
|
|
176
|
+
test: (tokens) => tokens[0] === "find" && tokens.includes("-delete"),
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "git-push-plus-refspec",
|
|
180
|
+
test: (tokens) => {
|
|
181
|
+
const git = gitSubcommand(tokens);
|
|
182
|
+
if (!git || git.sub !== "push")
|
|
183
|
+
return false;
|
|
184
|
+
return git.args.some((t) => t.startsWith("+") && t.length > 1);
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "git-stash-drop",
|
|
189
|
+
test: (tokens) => {
|
|
190
|
+
const git = gitSubcommand(tokens);
|
|
191
|
+
if (!git || git.sub !== "stash")
|
|
192
|
+
return false;
|
|
193
|
+
return git.args[0] === "drop" || git.args[0] === "clear";
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
/** Classify one Bash command string. Pure; never throws on odd input. */
|
|
198
|
+
export function classifyDestructiveBash(command) {
|
|
199
|
+
if (typeof command !== "string" || command.trim() === "")
|
|
200
|
+
return { destructive: false, rule: null };
|
|
201
|
+
const stripped = stripMessageArgs(command);
|
|
202
|
+
for (const segment of splitSimpleCommands(stripped)) {
|
|
203
|
+
const tokens = commandTokens(segment);
|
|
204
|
+
if (tokens.length === 0)
|
|
205
|
+
continue;
|
|
206
|
+
for (const rule of STRUCTURED_RULES) {
|
|
207
|
+
if (rule.test(tokens))
|
|
208
|
+
return { destructive: true, rule: rule.id };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const lower = stripped.toLowerCase();
|
|
212
|
+
const hit = DESTRUCTIVE_PATTERNS.find((p) => lower.includes(p.toLowerCase()));
|
|
213
|
+
if (hit !== undefined)
|
|
214
|
+
return { destructive: true, rule: `substring:${hit}` };
|
|
215
|
+
return { destructive: false, rule: null };
|
|
216
|
+
}
|
|
@@ -45,7 +45,7 @@ export function resolveSessionDir(sessionId) {
|
|
|
45
45
|
if (fromEnv)
|
|
46
46
|
return fromEnv;
|
|
47
47
|
const projectRoot = resolveProjectRoot();
|
|
48
|
-
const projectHash =
|
|
48
|
+
const projectHash = hashProjectRoot(projectRoot);
|
|
49
49
|
const base = join(resolveInstinctsRoot(), projectHash);
|
|
50
50
|
const scoped = sanitizeSessionId(sessionId);
|
|
51
51
|
return scoped ? join(base, "sessions", scoped) : base;
|
|
@@ -160,6 +160,10 @@ export function canonicalizeProjectRoot(p) {
|
|
|
160
160
|
export function canonicalizeFileKey(p) {
|
|
161
161
|
return canonicalizePath(p);
|
|
162
162
|
}
|
|
163
|
+
/** SHA-256[:12] of the canonical project root. C:/x, c:/x, and C:\\x share a bucket. */
|
|
164
|
+
export function hashProjectRoot(projectRoot) {
|
|
165
|
+
return createHash("sha256").update(canonicalizeProjectRoot(projectRoot)).digest("hex").slice(0, 12);
|
|
166
|
+
}
|
|
163
167
|
export function isFileCleared(state, filePath) {
|
|
164
168
|
return canonicalizeFileKey(filePath) in state.cleared_files;
|
|
165
169
|
}
|
|
@@ -386,7 +386,7 @@ const EXPERT_TOOL_ENTRIES = [
|
|
|
386
386
|
];
|
|
387
387
|
const MODE_METADATA = {
|
|
388
388
|
beginner: {
|
|
389
|
-
description: "Beginner mode: see what your agent learned, list its instincts, and request a session reflection. Bundles
|
|
389
|
+
description: "Beginner mode: see what your agent learned, list its instincts, and request a session reflection. Bundles the ship fast path plus grounding skills (gateguard, tdd-workflow, verification-loop) so one-defect delivery, research, tests, and verification happen by default — every edit starts from facts, not guesses.",
|
|
390
390
|
hooks: ["PreToolUse", "PostToolUse", "UserPromptSubmit"],
|
|
391
391
|
hookDescription: "Silently captures every tool call as observations and routes prompts to the matching skill via the route table. Lightweight and non-blocking.",
|
|
392
392
|
},
|
|
@@ -474,6 +474,11 @@ export function getPluginHooksConfig() {
|
|
|
474
474
|
command: "node \"${CLAUDE_PLUGIN_ROOT}/hooks/hook-pack.mjs\"",
|
|
475
475
|
timeout: hookTimeoutSeconds,
|
|
476
476
|
};
|
|
477
|
+
const configGuardCommand = {
|
|
478
|
+
type: "command",
|
|
479
|
+
command: "node \"${CLAUDE_PLUGIN_ROOT}/hooks/config-guard.mjs\"",
|
|
480
|
+
timeout: hookTimeoutSeconds,
|
|
481
|
+
};
|
|
477
482
|
const observeCommand = {
|
|
478
483
|
type: "command",
|
|
479
484
|
command: "node \"${CLAUDE_PLUGIN_ROOT}/bin/observe.mjs\"",
|
|
@@ -538,6 +543,12 @@ export function getPluginHooksConfig() {
|
|
|
538
543
|
// Edit/Read/etc. Warn-default (CLAUDE_CI_HOOKPACK_GATE) — never blocks
|
|
539
544
|
// until the operator opts in.
|
|
540
545
|
{ matcher: "Bash", hooks: [hookPackCommand] },
|
|
546
|
+
// config-guard watches the files that wire the guardrails
|
|
547
|
+
// (.claude/settings*.json, .mcp.json, hooks.json, .claude/hooks/,
|
|
548
|
+
// .claude/plugins/, .claude-plugin/) and the `claude plugin|mcp|config`
|
|
549
|
+
// CLI forms. Matcher keeps read-only tools off the hot path.
|
|
550
|
+
// Warn-default (CI_CONFIG_GUARD) — denies only under block.
|
|
551
|
+
{ matcher: "Bash|Edit|MultiEdit|Write|NotebookEdit", hooks: [configGuardCommand] },
|
|
541
552
|
],
|
|
542
553
|
PostToolUse: [{ hooks: [observeCommand] }],
|
|
543
554
|
UserPromptSubmit: [{ hooks: [routePromptCommand, recallBriefingCommand] }],
|
|
@@ -20,6 +20,7 @@ skill set on disk.
|
|
|
20
20
|
- `gateguard` — Enforces Law 1 (Research Before Executing) of the 7 Laws of AI Agent Discipline. Fact-forcing gate that blocks Edit/Write/Bash (including MultiEdit) and demands concrete investigation (importers, data schemas, user instruction) before allowing the action. Measurably improves output quality by +2.25 points vs ungated agents.
|
|
21
21
|
- `model-forward` — Enforces all 7 Laws as a standing stance — go with Claude Code and the model, not against it. Skills are scaffolding that merges into the model over time; the durable core is goal-driven execution (the higher the stated goal, the better) plus self-discipline guardrails.
|
|
22
22
|
- `recall` — Enforces Law 1 (Research Before Executing) of the 7 Laws of AI Agent Discipline. Makes past sessions first-class research material by searching the observation log with BM25 ranking, so 'have I hit this before?' is answerable before re-deriving a fix or repeating a mistake.
|
|
23
|
+
- `ship` — Enforces Law 1 (Research Before Executing), Law 3 (One Thing at a Time), and Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. Fix one defect through TDD and one PR, isolate unrelated dirty checkouts in an owner-locked clean worktree, and return only eligible clean checkouts to the detected default branch. Use for urgent hotfixes, bug fixes from a messy checkout, or requests to ship one defect without stashing current work.
|
|
23
24
|
- `tdd-workflow` — Enforces Law 3 (One Thing at a Time) and Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.
|
|
24
25
|
- `verification-loop` — Enforces Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. A comprehensive verification system for agent coding sessions covering build, types, lint, tests, security, and diff with a PASS/FAIL report.
|
|
25
26
|
|
|
@@ -33,7 +34,6 @@ skill set on disk.
|
|
|
33
34
|
- `reconcile` — Enforces Law 1 (Research Before Executing) of the 7 Laws of AI Agent Discipline. Establishes git ground truth — branch, status, stashes, worktrees, ahead/behind — before any mutation, halts on protected or destructive operations, then carries the known-good state through to a landed PR: stage by filename, commit one concern, push the feature branch, verify the push landed, open the PR, and after the PR merges fast-forward the default branch and check it out.
|
|
34
35
|
- `recovery-classification` — Enforces Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. After any failure in the verification ladder or auto-loop, classify the failure class before retrying — provider, tool-schema, deterministic-policy, git, worktree, runtime — so retry-vs-pause-vs-self-heal-vs-stop is an intentional decision, not a generic 'try again'.
|
|
35
36
|
- `roast` — Enforces Law 1 (Research Before Executing) of the 7 Laws of AI Agent Discipline. Convene a 5-persona adversarial council (Contrarian, Expansionist, Logician, Researcher, Buyer) that attacks an idea from every angle, then a Judge returns one GO / RESHAPE / KILL verdict plus the cheapest 48-hour test to de-risk it — so you pressure-test an idea before sinking time into building the wrong thing.
|
|
36
|
-
- `safety-guard` — Enforces Law 3 (One Thing at a Time) of the 7 Laws of AI Agent Discipline by scoping edits to a directory and blocking destructive shell commands. Use this skill to prevent destructive operations when working on production systems or running agents autonomously.
|
|
37
37
|
- `simplicity-review` — Enforces Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. Reviews the current diff for over-engineering (code that could reuse an existing file, a stdlib or native feature, or fewer lines) and reports trim findings without touching code, so 'it works' is never mistaken for 'it is the minimum that works'.
|
|
38
38
|
- `skill-distillation` — Enforces Law 7 (Learn From Every Session) of the 7 Laws of AI Agent Discipline. Distills repeated successful tool sequences into reusable draft instincts, so a pattern that worked three times becomes a captured recipe instead of being re-derived from scratch every session.
|
|
39
39
|
- `state-reconciliation` — Enforces Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. Pre-dispatch invariant: reconcile DB-vs-disk-vs-memory state before any unit runs, so a stale flag, missing artifact, or out-of-sync row never re-dispatches a unit that already completed or never started.
|
|
@@ -42,6 +42,8 @@ Before executing, state:
|
|
|
42
42
|
- Build passes
|
|
43
43
|
- You can explain what changed in one sentence
|
|
44
44
|
|
|
45
|
+
"Done" is an audit, not a hope: settle the actual result against the Verification you stipulated in Law 2.
|
|
46
|
+
|
|
45
47
|
## Law 5: Reflect After Every Session
|
|
46
48
|
|
|
47
49
|
After non-trivial tasks:
|
|
@@ -59,6 +61,8 @@ After non-trivial tasks:
|
|
|
59
61
|
|
|
60
62
|
The "Rule to add" field feeds Law 7 — it becomes an instinct with 0.6 starting confidence.
|
|
61
63
|
|
|
64
|
+
This block is the ledger: what worked and what failed are the session's gains and losses; the rule to add is what you carry into the session you will not be in.
|
|
65
|
+
|
|
62
66
|
The "Iteration — Next best recommendations" field feeds Law 6. List the **top 3 ranked** core-development moves based on the current code state — what to build, fix, refactor, or investigate next so the feature/system advances. Item #1 is the strongest recommendation; #2 and #3 are alternative directions the user can pick from. NOT git plumbing (commit, push, PR), NOT pure CI ceremony (run tests, type-check), NOT deploy steps. Those belong in the end-of-run summary, not here.
|
|
63
67
|
|
|
64
68
|
Format per item: `<verb> <object at path:line> (<why, one clause grounded in current context>)`.
|
|
@@ -157,7 +157,7 @@ The rollback command is **printed**, not run. The skill's job is to give the ope
|
|
|
157
157
|
- `finishing-a-development-branch` (vendored, third-party/superpowers/) — runs first; reports the merge. This skill runs after.
|
|
158
158
|
- `verification-loop` — same Law 4 family; this skill is the deploy-seam specialization
|
|
159
159
|
- `proceed-with-the-recommendation` — routing-table row for "Merge / close branch" should pair `finishing-a-development-branch` with this skill when the project is auto-deploy
|
|
160
|
-
- `
|
|
160
|
+
- `gateguard` — orthogonal; gateguard gates the destructive op, this verifies post-deploy state
|
|
161
161
|
|
|
162
162
|
## Close-the-Loop Rule
|
|
163
163
|
|
|
@@ -77,7 +77,7 @@ Before creating {file_path}, present these facts:
|
|
|
77
77
|
|
|
78
78
|
### Destructive Bash Gate (every destructive command)
|
|
79
79
|
|
|
80
|
-
Triggers on
|
|
80
|
+
Triggers on structured rules that ignore flag order and spelling — `rm` with any recursive plus any force flag (`rm -r -f`, `rm -Rf`, `rm --recursive --force`), `git clean` with a force flag and no dry run, `git checkout -- <path>` or `git checkout .`, `git restore <path>` unless it is `--staged` only, `find … -delete`, `git push` with a `+refspec`, `git stash drop|clear` — plus the original substring list (`rm -rf`, `git reset --hard`, `git push --force`, `git branch -D`, `drop table`, `truncate `, `Remove-Item -Recurse`, etc.). Each command is judged after `&&`, `||`, `|`, `;` splitting, and a commit message, PR body or title value is blanked first so prose never trips it. The deny reason prints `Matched rule: <id>` so a block is explainable; the classifier is `lib/destructive-bash.mjs`. Plain file writes through Bash (`cat > file`, `sed -i`) are deliberately not gated.
|
|
81
81
|
|
|
82
82
|
```
|
|
83
83
|
1. List all files/data this command will modify or delete
|
|
@@ -154,10 +154,24 @@ The inline `_gateguard_facts_presented: true` retry still works on harnesses tha
|
|
|
154
154
|
|
|
155
155
|
Set the `CI_GATEGUARD_EXCLUDE` environment variable to opt specific low-risk paths out of the gate entirely — an LLM-maintained prose wiki, a generated scratch directory, anything where the fact-forcing pause costs more than it saves. The value is a comma-separated list of path substrings, each matched case-insensitively against the forward-slash-normalized file path, so `/mywiki/` excludes `D:\Vault\MyWiki\notes\x.md`. Unset or empty (the default) changes nothing: every mutating file call is gated exactly as before, and a call that touches a mix of excluded and non-excluded paths still gates the non-excluded ones. Set it per project in `.claude/settings.json` under `env`, or globally in `~/.claude/settings.json`.
|
|
156
156
|
|
|
157
|
+
An exclusion is never silent. When every target of a call is excluded, `hooks/gateguard.mjs` still allows it but prints one stderr line naming the fragment that matched (`gateguard: skipped by CI_GATEGUARD_EXCLUDE (fragment "docs/wiki" matched docs/wiki/page.md)`). A catch-all fragment that every path contains (`/`, `.`, any single character) is honoured too, but the line says what it really is: the file gate is off for this session. Destructive Bash is never excluded. If `/verify-install` reports the probe write went through, check this variable before concluding the hook is not wired.
|
|
158
|
+
|
|
157
159
|
### Locking edits to the current repo
|
|
158
160
|
|
|
159
161
|
A fact-list can't catch a wrong-repo or wrong-worktree write — you can present perfect facts about the wrong file, in the wrong checkout. Set `CI_GATEGUARD_TARGET_LOCK=block` to make the runtime hook (`hooks/gateguard.mjs`) refuse any mutating call whose **absolute** target canonicalizes outside the session project root (`CLAUDE_PROJECT_DIR`, or the git toplevel). Relative paths resolve under the current directory (= the root) and always pass; only an absolute path into a different tree is denied, and the deny reason names both the stray target and the expected root. This runs before the fact gate and independent of clearance — a wrong-repo write is wrong even with facts. Unset (the default) checks nothing, so legitimate out-of-root edits (`~/.claude`, a `/tmp` scratch file, a sibling repo) are unaffected; turn it on per session in a multi-worktree or headless run where cross-repo writes are the real risk. Paths already covered by `CI_GATEGUARD_EXCLUDE` are never target-locked.
|
|
160
162
|
|
|
163
|
+
### Migrating from `safety-guard` (retired 2026-08-07)
|
|
164
|
+
|
|
165
|
+
`safety-guard` was a prose-only tier-2 skill describing three "modes" it never implemented — no hook, no command, no logger ever shipped with it. Every mode it described is already enforced here by the runtime hook (`hooks/gateguard.mjs`):
|
|
166
|
+
|
|
167
|
+
| Retired `safety-guard` mode | What enforces it now |
|
|
168
|
+
|---|---|
|
|
169
|
+
| Careful — warn on `rm -rf`, `git push --force`, `git reset --hard`, `DROP TABLE`, `chmod 777`, `--no-verify` … | [Destructive Bash Gate](#destructive-bash-gate-every-destructive-command). Its pattern set is a superset of the retired watch list, and it blocks rather than warns. |
|
|
170
|
+
| Freeze — restrict writes to one directory tree | `CI_GATEGUARD_TARGET_LOCK=block` (above). Canonicalizes the absolute target against the session project root and refuses strays — before the fact gate, independent of clearance. |
|
|
171
|
+
| Guard — careful + freeze together | Both of the above; they compose. Narrow the surface further with `CI_GATEGUARD_EXCLUDE`. |
|
|
172
|
+
|
|
173
|
+
If you previously opted into `safety-guard` for autonomous or production-adjacent runs, set `CI_GATEGUARD_TARGET_LOCK=block` for that session instead. That is a real refusal, not a checklist the agent can talk itself out of.
|
|
174
|
+
|
|
161
175
|
### Limitations and guarantees
|
|
162
176
|
|
|
163
177
|
- **Honor system.** Clearance is recorded by `ci_gateguard_clear`, the `gateguard-clear.mjs` CLI, a manual state-file write, or the inline `_gateguard_facts_presented` flag where the harness allows it (see "Clearing the gate" above). The hook can't verify the investigation actually happened; the 50-file cap — counted per session — bounds damage from stuck loops or rogue agents.
|
|
@@ -188,5 +202,6 @@ The standalone `gateguard-ai` Python/CLI package referenced in earlier drafts of
|
|
|
188
202
|
|
|
189
203
|
## Related Skills
|
|
190
204
|
|
|
191
|
-
- `safety
|
|
205
|
+
- `worktree-safety` — validates the worktree root before a source write; this skill gates the write itself
|
|
206
|
+
- `reconcile` — git ground truth before a destructive git action
|
|
192
207
|
- `code-reviewer` — Post-edit review (GateGuard is pre-edit investigation)
|
|
@@ -177,6 +177,5 @@ git branch -d <type>/<slug> # delete the merged feature branch (safe
|
|
|
177
177
|
|
|
178
178
|
- **`recall`** (Law 1) — before a risky git op, recall whether the same operation failed on this repo before.
|
|
179
179
|
- **`gateguard`** (Law 1) — the runtime gate (`hooks/gateguard.mjs`); `reconcile` is the procedure you run once a destructive git action is in play.
|
|
180
|
-
- **`safety-guard`** — destructive-operation guardrails for production and autonomous runs.
|
|
181
180
|
- **`audit`** (Law 4) — when an audit ends in a fix, `reconcile` is the safe path from branch to landed PR.
|
|
182
181
|
- **`commit-commands:commit-push-pr`** — the external-plugin equivalent of the commit → push → PR tail; `reconcile` reimplements it inline so the flow works with no companion installed. For a TDD-gated single-defect variant, use `/ship`.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ship
|
|
3
|
+
tier: "1"
|
|
4
|
+
description: >-
|
|
5
|
+
Enforces Law 1 (Research Before Executing), Law 3 (One Thing at a Time), and Law 4 (Verify Before Reporting) of the 7 Laws of AI Agent Discipline. Fix one defect through TDD and one PR, isolate unrelated dirty checkouts in an owner-locked clean worktree, and return only eligible clean checkouts to the detected default branch. Use for urgent hotfixes, bug fixes from a messy checkout, or requests to ship one defect without stashing current work.
|
|
6
|
+
origin: continuous-improvement
|
|
7
|
+
user-invocable: true
|
|
8
|
+
disable-model-invocation: true
|
|
9
|
+
argument-hint: "[one-line defect description]"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Ship
|
|
13
|
+
|
|
14
|
+
Fix one defect, open one PR, and hand it back for review. Use `release-train` for stacked multi-PR rollouts and `proceed-with-the-recommendation` for an arbitrary recommendation list.
|
|
15
|
+
|
|
16
|
+
Preserve branch protection. Never force-push, auto-merge, or deploy from this skill.
|
|
17
|
+
|
|
18
|
+
## Request
|
|
19
|
+
|
|
20
|
+
Treat `$ARGUMENTS` as the defect request when supplied. Otherwise use the single concrete defect from the active conversation. Halt and ask for one narrower defect when the request is empty, ambiguous, or contains more than one concern.
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
Run these steps in order:
|
|
25
|
+
|
|
26
|
+
1. **Capture ground truth and resolve the base**: run the read-only `reconcile` probes. Record the initiating checkout's absolute root, branch, HEAD, real tracked drift, staged drift, untracked files, in-progress Git operations, registered worktrees, and task ownership. Ownership requires the current session ID in the authoritative worktree lease or an equivalent active harness task ledger that names this exact checkout; merely starting there is not proof. Set `return_allowed=true` only when the initiating checkout is clean, that ownership proof matches the current session, and no other task reserves it. Without such a lease or ledger, fail closed with `return_allowed=false`. A dirty-tree blocker may continue only through the isolated path in step 2 after the changes are confirmed unrelated. Every other blocker still halts. After classification, make the fetch below the first allowed repository mutation and refresh the remote before choosing a base:
|
|
27
|
+
```
|
|
28
|
+
git fetch --prune origin
|
|
29
|
+
git ls-remote --symref origin HEAD
|
|
30
|
+
```
|
|
31
|
+
Use the live `refs/heads/<base>` returned for `HEAD`. Validate it with `git check-ref-format --branch "<base>"`. Compare it with `refs/remotes/origin/HEAD`, but never let a stale local symbolic ref override the live result. The query must also return the remote HEAD commit. If it succeeds without a symbolic ref, compare that remote HEAD commit with the resolved tips of `origin/main` and `origin/master`, and require exactly one matching candidate. Zero or multiple matches are ambiguous: halt instead of guessing. Halt on a fetch/query failure or if no verified base exists. Confirm `origin/<base>` resolves after the fetch, record its immutable commit as `<base-sha>`, require a full hexadecimal commit ID, and require that it equals the verified remote HEAD commit. Pin worktree creation to `<base-sha>`, not the mutable remote-tracking name.
|
|
32
|
+
2. **Select a safe checkout**:
|
|
33
|
+
- Always perform the defect work in a separate isolated worktree created from the pinned `<base-sha>`. Do not reuse the initiating checkout, even when it is clean. One isolation path keeps ownership, retention, return, and cleanup behavior consistent.
|
|
34
|
+
- If dirty changes are not clearly unrelated to the defect, halt and ask. Never guess which changes belong to whom.
|
|
35
|
+
- Preserve the initiating checkout until the return decision in step 8. When it is dirty with unrelated work, protected, stale, ahead, or owned by another task, do not stash, switch, reset, clean, or copy its changes. Choose an absent absolute sibling or temporary path. Generate the feature branch from ASCII lowercase letters, digits, slash, underscore, and hyphen only (`[a-z0-9/_-]+`), require an alphanumeric first character, and validate the final name before any lookup. Prove the proposed branch is absent locally, then query the remote successfully and require empty output before using the name:
|
|
36
|
+
```
|
|
37
|
+
git check-ref-format --branch "<feature-branch>"
|
|
38
|
+
git show-ref --verify "refs/heads/<feature-branch>"
|
|
39
|
+
git ls-remote --heads origin "refs/heads/<feature-branch>"
|
|
40
|
+
```
|
|
41
|
+
The local command must report no ref. The remote command must complete without a network/authentication error and return no matching ref. If either branch exists, choose another unique name. Create a no-upstream worktree with an atomic owner lock:
|
|
42
|
+
```
|
|
43
|
+
git worktree add --no-track --lock --reason "owner=<session-id>; purpose=/ship" -b "<feature-branch>" "<worktree-path>" "<base-sha>"
|
|
44
|
+
```
|
|
45
|
+
- Use the current harness session ID as `<session-id>`. If it is unavailable, generate a unique recorded run token before creating the worktree and reuse that exact token through handoff and cleanup. Treat the lock reason as an advisory coordination ledger for compliant sessions, not as a filesystem write lock. Run `worktree-safety` in the new checkout and confirm its resolved root, `.git` pointer, registration, branch, pinned HEAD, and lock reason all match the current session. A missing or foreign owner blocks work. Recheck that envelope and observable branch, HEAD, and diff state before every source mutation. Halt when another writer cannot be excluded.
|
|
46
|
+
3. **Reproduce (RED)**: use `tdd-workflow` to write a failing test that reproduces the defect and watch it fail. Delete any pre-test implementation code.
|
|
47
|
+
4. **Fix (GREEN)**: write the minimal change that makes the failing test pass, then watch it pass. Keep one concern only.
|
|
48
|
+
5. **Verify**: use `verification-loop` to run the project's verify ladder, including build, types, and relevant tests. A green build proves only the mechanism. Confirm the original defect no longer reproduces.
|
|
49
|
+
6. **Commit**: make one single-concern commit, staged by explicit filename. Never use `git add -A` or `git add .`. Recheck the branch, HEAD, owner lock, and real diff immediately before staging and committing. Use a Windows-safe commit message with a single-line `-m`, repeated `-m` paragraphs, or `git commit -F <tempfile>`. Do not use multi-line here-docs or here-strings.
|
|
50
|
+
7. **Push and open the PR**: confirm the feature branch has no upstream to the protected base. Re-run the remote collision query immediately before pushing, require empty output, then push its name explicitly without force:
|
|
51
|
+
```
|
|
52
|
+
git ls-remote --heads origin "refs/heads/<feature-branch>"
|
|
53
|
+
git push -u origin "<feature-branch>"
|
|
54
|
+
```
|
|
55
|
+
Verify the remote tip equals local HEAD. Use `commit-commands:commit-push-pr` only when it accepts the explicit base and head below; otherwise open the PR directly and cite the plan or issue:
|
|
56
|
+
```
|
|
57
|
+
gh pr create --base "<base>" --head "<feature-branch>"
|
|
58
|
+
gh pr view "<pr-number-or-url>" --json baseRefName,headRefName,headRefOid
|
|
59
|
+
```
|
|
60
|
+
Require `baseRefName=<base>`, `headRefName=<feature-branch>`, and `headRefOid` equal to local HEAD. Record that exact PR URL or number for cleanup. A mismatch halts. Do not merge it.
|
|
61
|
+
8. **Return before stopping**:
|
|
62
|
+
- Confirm the fix checkout is clean and every commit is pushed. If the captured `return_allowed` value is true, immediately revalidate that the initiating checkout remains clean, current-session-owned, and unreserved. Freeze the final decision and its reason. Any drift changes the final value to false.
|
|
63
|
+
- Persist a local cleanup receipt at `<git-common-dir>/continuous-improvement/ship-receipts/<pr-number>.json` with the PR URL and number, base, base SHA, feature branch, feature tip SHA, absolute worktree path, owner token, initiating checkout path, and final `return_allowed` decision and reason. Write a sibling temporary file first, atomically rename it into place, then read and parse it back before continuing. Keep the local path and owner token out of the public PR body and comments. If later drift appears before a return mutation, atomically downgrade the receipt to `return_allowed=false`, read it back, and leave the initiating checkout unchanged.
|
|
64
|
+
- Only when the final `return_allowed=true`, meaning the initiating checkout was clean, owned by the current session, and not reserved by another task, consider returning it. Fetch immediately before any switch, revalidate the remote base, and prove no other worktree has `<base>` checked out:
|
|
65
|
+
```
|
|
66
|
+
git fetch --prune origin
|
|
67
|
+
```
|
|
68
|
+
If local `<base>` exists and is not the initiating checkout's current branch, require it to be an ancestor of `origin/<base>`, update that branch ref before switching, then switch only after every network and ref check has passed:
|
|
69
|
+
```
|
|
70
|
+
git merge-base --is-ancestor "refs/heads/<base>" "origin/<base>"
|
|
71
|
+
git branch -f "<base>" "origin/<base>"
|
|
72
|
+
git switch "<base>"
|
|
73
|
+
```
|
|
74
|
+
If local `<base>` does not exist, create it without switching, then switch:
|
|
75
|
+
```
|
|
76
|
+
git branch --track "<base>" "origin/<base>"
|
|
77
|
+
git switch "<base>"
|
|
78
|
+
```
|
|
79
|
+
If the initiating checkout is already on `<base>`, require ancestry and use `git merge --ff-only "origin/<base>"`; a non-fast-forward halts without switching branches. Verify local `<base>` equals `origin/<base>`. If the fetch, remote-base validation, checked-out-elsewhere check, ancestry preflight, branch update, branch creation, fast-forward, or switch fails, leave the initiating checkout's branch and files unchanged and report the blocker. Never switch first and pull afterward.
|
|
80
|
+
- When `return_allowed=false`, leave the initiating checkout's branch and path unchanged even if it appears clean later. Another task may own that state. Report the recorded reason instead of switching it.
|
|
81
|
+
- A dirty initiating checkout is the exception: leave its branch and files exactly as found. Return the shell to that path, but do not carry its changes onto `<base>`. Report that default-branch return is intentionally blocked by preserved local work.
|
|
82
|
+
- During PR review, only the recorded owner or an explicit operator-confirmed handoff may change the retained fix worktree. After every authorized review-fix commit and push, rerun the verification ladder, require a clean worktree, verify the remote feature tip equals local HEAD, and query the same PR again for base, head, and `headRefOid`. Atomically replace the receipt's feature tip SHA with that verified `headRefOid`, then read and parse the receipt back. Halt and retain the worktree if any verification or receipt refresh fails.
|
|
83
|
+
- Stop with the PR open for human review. Keep an isolated fix worktree registered and owner-locked until the PR is merged so review fixes remain safe.
|
|
84
|
+
9. **Clean up after the PR merges**: run cleanup from the initiating checkout or another retained worktree, never from inside the worktree being removed. Refresh remote state and verify the actual PR merge commit is contained in the detected base, including for squash merges:
|
|
85
|
+
```
|
|
86
|
+
git fetch --prune origin
|
|
87
|
+
gh pr view "<pr-number-or-url>" --json state,mergeCommit,baseRefName,headRefName,headRefOid
|
|
88
|
+
git merge-base --is-ancestor "<merge-sha>" "origin/<base>"
|
|
89
|
+
```
|
|
90
|
+
Read `<git-common-dir>/continuous-improvement/ship-receipts/<pr-number>.json` and compare every field with the registered worktree, current refs, PR response, and initiating checkout before cleanup. Halt on a missing, malformed, or mismatched receipt. Halt unless the PR state is `MERGED`, its base and head still match the recorded receipt, its pre-merge `headRefOid` identifies the pushed feature tip, and the ancestry check succeeds. Cleanup may proceed only as the original owner recorded in the local receipt, or after an explicit operator-confirmed handoff that proves the original session is inactive, replaces the owner token in that receipt atomically, and reruns `worktree-safety`. Never silently treat a foreign lock as stale. Confirm the isolated worktree is clean, its HEAD equals both the receipt's feature tip SHA and the PR `headRefOid`, it still carries the authorized owner lock, and it has no observed competing writer. A missing remote feature ref after fetch is expected when GitHub deleted the merged branch; if that ref still exists, require its tip to equal the receipt's feature tip. Recheck immediately before unlock, then release the lock and remove only the named worktree without pausing between commands:
|
|
91
|
+
```
|
|
92
|
+
git worktree unlock "<worktree-path>"
|
|
93
|
+
git worktree remove "<worktree-path>"
|
|
94
|
+
```
|
|
95
|
+
If state shifts or removal fails, halt and re-establish ownership instead of forcing. Do not run repository-wide pruning. Return or refresh the initiating checkout on `<base>` only when its recorded `return_allowed` decision permits it, using the same fetch-before-switch, checked-out-elsewhere, ancestry, pre-update, create-if-missing, and already-on-base procedures from step 8, then verify local and remote HEADs match. Delete the local feature branch only if `git branch -d "<feature-branch>"` accepts it. Squash merges may make safe deletion refuse; retain and report the branch instead of forcing it. Remove the local receipt only after cleanup and every permitted return check succeeds. A dirty or foreign-owned initiating checkout remains untouched.
|
|
96
|
+
10. **Deploy receipt (advisory)**: after the human merge and deployment, `deploy-receipt` may verify that the deployed SHA matches the merge SHA. This skill does not deploy.
|
|
97
|
+
|
|
98
|
+
## Hard stops
|
|
99
|
+
|
|
100
|
+
- The defect description is ambiguous or includes multiple concerns.
|
|
101
|
+
- Dirty changes may overlap the defect or their ownership is unclear.
|
|
102
|
+
- The live remote default branch cannot be resolved and refreshed.
|
|
103
|
+
- The remote default is ambiguous because the live HEAD query has zero or multiple matching `main` or `master` candidates.
|
|
104
|
+
- The selected feature branch or worktree path already exists.
|
|
105
|
+
- The remote feature-branch collision query fails or returns an existing ref.
|
|
106
|
+
- `worktree-safety` cannot prove the new checkout is registered, aligned, and owner-locked to this session.
|
|
107
|
+
- The branch, HEAD, or owner lock shifts after the ground-truth snapshot.
|
|
108
|
+
- Any verification step fails with a non-obvious fix.
|
|
109
|
+
- The fix would touch more than 15 non-generated files. Split it or use `release-train`.
|
|
110
|
+
- A push would target a protected branch or the remote feature tip cannot be verified.
|
|
111
|
+
- Post-merge state or merge-commit ancestry cannot be verified.
|
|
112
|
+
- Cleanup would require force or discard dirty, untracked, or unpushed work.
|
|
113
|
+
|
|
114
|
+
## Refuse these anti-patterns
|
|
115
|
+
|
|
116
|
+
- **Stashing unrelated work**: an isolated worktree removes the need.
|
|
117
|
+
- **Moving dirty changes across branches**: preservation takes priority over returning to the default branch.
|
|
118
|
+
- **Hardcoding `main`**: detect the live remote default and support `main` or `master`.
|
|
119
|
+
- **Giving the feature branch a protected upstream**: create with `--no-track` and push the feature name explicitly.
|
|
120
|
+
- **Force cleanup**: never use `git worktree remove --force`, `git branch -D`, `git reset --hard`, or `git clean -fd`.
|
|
121
|
+
- **Repository-wide cleanup**: remove only the named worktree created by this run.
|
|
122
|
+
- **Foreign checkout return**: never switch a checkout without a recorded current-session ownership decision.
|
|
123
|
+
- **Silent lock takeover**: require explicit operator-confirmed handoff when cleanup runs under a different session.
|
|
124
|
+
- **Auto-merge**: never merge the PR this skill opens, even when CI is green.
|
|
125
|
+
- **Deploy**: never run a deploy. `deploy-receipt` only verifies after a human merges and deploys.
|
|
126
|
+
- **Bypass**: never use `--admin`, `--force`, or `--no-verify`.
|
|
127
|
+
- **Bundled concerns**: log unrelated defects as deferred follow-ups instead of adding them to the commit.
|
|
128
|
+
|
|
129
|
+
## Composition
|
|
130
|
+
|
|
131
|
+
Route through `reconcile`, mandatory `worktree-safety`, `tdd-workflow`, `verification-loop`, `commit-commands:commit-push-pr`, the safe-return and post-merge cleanup rules above, then advisory `deploy-receipt`. Fall back to equivalent inline checks when a companion skill is unavailable, except ownership: no proven single-writer ledger means no source mutation.
|
|
132
|
+
|
|
133
|
+
## Example
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
/ship registration form accepts a negative deposit amount
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If the initiating checkout contains unrelated feature work, leave it untouched. Create an owner-locked no-upstream worktree from the verified `<base-sha>`, reproduce and fix the defect, verify and push one commit, open one explicitly targeted PR, and return the shell to the initiating path. After the PR merges, read the local receipt, freshly verify the merge commit, and remove only the clean temporary worktree. Switch the initiating checkout to the default branch only when that checkout is clean and current-session-owned.
|
package/plugins/expert.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "continuous-improvement",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0",
|
|
4
4
|
"mode": "expert",
|
|
5
5
|
"description": "Expert mode: tune confidence, manage instincts, and persist plans on disk. Adds safety, token-budget, and strategic-compact skills plus the /learn-eval command so long sessions stay sharp and learnings survive context resets.",
|
|
6
6
|
"tools": [
|
package/skills/README.md
CHANGED
|
@@ -22,16 +22,16 @@ These add concrete enforcement to the 7 Laws. Tier-1 skills are the always-on mi
|
|
|
22
22
|
| `gateguard` | PreToolUse fact-forcing gate that blocks Edit/Write/destructive Bash until concrete investigation is presented | Law 1 (Research) |
|
|
23
23
|
| `model-forward` | Standing stance: go with Claude Code and the model, not against it — skills are scaffolding that merges into the model; the durable core is goal-driven execution + guardrails | All 7 Laws (stance) |
|
|
24
24
|
| `recall` | BM25 search over the observation log so "have I hit this before?" is answerable before re-deriving a fix | Law 1 (Research) |
|
|
25
|
+
| `ship` | Single-defect delivery path that preserves unrelated dirty work in an owner-locked worktree, verifies through TDD, opens one PR, and safely returns eligible clean checkouts to `main` or `master` | Laws 1, 3, and 4 |
|
|
25
26
|
| `tdd-workflow` | RED→GREEN→REFACTOR enforcement, 80%+ coverage gate across unit/integration/E2E | Law 3 (One Thing), Law 4 (Verify) |
|
|
26
27
|
| `verification-loop` | Six-phase verification (build, types, lint, tests, security, diff) with a structured PASS/FAIL report | Law 4 (Verify Before Reporting) |
|
|
27
28
|
|
|
28
29
|
## Tier 2 — additional skills for **expert** mode
|
|
29
30
|
|
|
30
|
-
Tier-2 skills layer on top of tier-1 for users running `npx continuous-improvement install --mode expert`. They cover
|
|
31
|
+
Tier-2 skills layer on top of tier-1 for users running `npx continuous-improvement install --mode expert`. They cover response-depth control and context-window discipline that matter once an agent runs longer or more aggressively. Autonomous-mode write safety lives in tier-1 `gateguard` (`CI_GATEGUARD_TARGET_LOCK=block`), which replaced the retired `safety-guard` skill on 2026-08-07.
|
|
31
32
|
|
|
32
33
|
| Skill | What it does | When it pays off |
|
|
33
34
|
|-------|--------------|------------------|
|
|
34
|
-
| `safety-guard` | Three-mode runtime guard (careful/freeze/guard) that blocks destructive commands and locks edits to a directory | Autonomous loops, prod systems, `--dangerously-skip-permissions` sessions |
|
|
35
35
|
| `token-budget-advisor` | Heuristic input/output token estimator that offers 25%/50%/75%/100% depth choices before answering | Long sessions where response size matters |
|
|
36
36
|
| `strategic-compact` | Manual phase-boundary checklist for deciding when to run `/compact` (research→plan, plan→implement, debug→next) instead of relying on arbitrary auto-compaction | Multi-phase tasks that approach context limits |
|
|
37
37
|
| `wild-risa-balance` | Decision-framing lens that pairs WILD (Wild/Imaginative/Limitless/Disruptive) generation with RISA (Realistic/Important/Specific/Agreeable) execution, used to split recommendation lists into bold pilots above a safe baseline | Multi-item recommendation blocks where bold options keep losing to safe ones in a flat list |
|
package/skills/deploy-receipt.md
CHANGED
|
@@ -157,7 +157,7 @@ The rollback command is **printed**, not run. The skill's job is to give the ope
|
|
|
157
157
|
- `finishing-a-development-branch` (vendored, third-party/superpowers/) — runs first; reports the merge. This skill runs after.
|
|
158
158
|
- `verification-loop` — same Law 4 family; this skill is the deploy-seam specialization
|
|
159
159
|
- `proceed-with-the-recommendation` — routing-table row for "Merge / close branch" should pair `finishing-a-development-branch` with this skill when the project is auto-deploy
|
|
160
|
-
- `
|
|
160
|
+
- `gateguard` — orthogonal; gateguard gates the destructive op, this verifies post-deploy state
|
|
161
161
|
|
|
162
162
|
## Close-the-Loop Rule
|
|
163
163
|
|
package/skills/gateguard.md
CHANGED
|
@@ -77,7 +77,7 @@ Before creating {file_path}, present these facts:
|
|
|
77
77
|
|
|
78
78
|
### Destructive Bash Gate (every destructive command)
|
|
79
79
|
|
|
80
|
-
Triggers on
|
|
80
|
+
Triggers on structured rules that ignore flag order and spelling — `rm` with any recursive plus any force flag (`rm -r -f`, `rm -Rf`, `rm --recursive --force`), `git clean` with a force flag and no dry run, `git checkout -- <path>` or `git checkout .`, `git restore <path>` unless it is `--staged` only, `find … -delete`, `git push` with a `+refspec`, `git stash drop|clear` — plus the original substring list (`rm -rf`, `git reset --hard`, `git push --force`, `git branch -D`, `drop table`, `truncate `, `Remove-Item -Recurse`, etc.). Each command is judged after `&&`, `||`, `|`, `;` splitting, and a commit message, PR body or title value is blanked first so prose never trips it. The deny reason prints `Matched rule: <id>` so a block is explainable; the classifier is `lib/destructive-bash.mjs`. Plain file writes through Bash (`cat > file`, `sed -i`) are deliberately not gated.
|
|
81
81
|
|
|
82
82
|
```
|
|
83
83
|
1. List all files/data this command will modify or delete
|
|
@@ -154,10 +154,24 @@ The inline `_gateguard_facts_presented: true` retry still works on harnesses tha
|
|
|
154
154
|
|
|
155
155
|
Set the `CI_GATEGUARD_EXCLUDE` environment variable to opt specific low-risk paths out of the gate entirely — an LLM-maintained prose wiki, a generated scratch directory, anything where the fact-forcing pause costs more than it saves. The value is a comma-separated list of path substrings, each matched case-insensitively against the forward-slash-normalized file path, so `/mywiki/` excludes `D:\Vault\MyWiki\notes\x.md`. Unset or empty (the default) changes nothing: every mutating file call is gated exactly as before, and a call that touches a mix of excluded and non-excluded paths still gates the non-excluded ones. Set it per project in `.claude/settings.json` under `env`, or globally in `~/.claude/settings.json`.
|
|
156
156
|
|
|
157
|
+
An exclusion is never silent. When every target of a call is excluded, `hooks/gateguard.mjs` still allows it but prints one stderr line naming the fragment that matched (`gateguard: skipped by CI_GATEGUARD_EXCLUDE (fragment "docs/wiki" matched docs/wiki/page.md)`). A catch-all fragment that every path contains (`/`, `.`, any single character) is honoured too, but the line says what it really is: the file gate is off for this session. Destructive Bash is never excluded. If `/verify-install` reports the probe write went through, check this variable before concluding the hook is not wired.
|
|
158
|
+
|
|
157
159
|
### Locking edits to the current repo
|
|
158
160
|
|
|
159
161
|
A fact-list can't catch a wrong-repo or wrong-worktree write — you can present perfect facts about the wrong file, in the wrong checkout. Set `CI_GATEGUARD_TARGET_LOCK=block` to make the runtime hook (`hooks/gateguard.mjs`) refuse any mutating call whose **absolute** target canonicalizes outside the session project root (`CLAUDE_PROJECT_DIR`, or the git toplevel). Relative paths resolve under the current directory (= the root) and always pass; only an absolute path into a different tree is denied, and the deny reason names both the stray target and the expected root. This runs before the fact gate and independent of clearance — a wrong-repo write is wrong even with facts. Unset (the default) checks nothing, so legitimate out-of-root edits (`~/.claude`, a `/tmp` scratch file, a sibling repo) are unaffected; turn it on per session in a multi-worktree or headless run where cross-repo writes are the real risk. Paths already covered by `CI_GATEGUARD_EXCLUDE` are never target-locked.
|
|
160
162
|
|
|
163
|
+
### Migrating from `safety-guard` (retired 2026-08-07)
|
|
164
|
+
|
|
165
|
+
`safety-guard` was a prose-only tier-2 skill describing three "modes" it never implemented — no hook, no command, no logger ever shipped with it. Every mode it described is already enforced here by the runtime hook (`hooks/gateguard.mjs`):
|
|
166
|
+
|
|
167
|
+
| Retired `safety-guard` mode | What enforces it now |
|
|
168
|
+
|---|---|
|
|
169
|
+
| Careful — warn on `rm -rf`, `git push --force`, `git reset --hard`, `DROP TABLE`, `chmod 777`, `--no-verify` … | [Destructive Bash Gate](#destructive-bash-gate-every-destructive-command). Its pattern set is a superset of the retired watch list, and it blocks rather than warns. |
|
|
170
|
+
| Freeze — restrict writes to one directory tree | `CI_GATEGUARD_TARGET_LOCK=block` (above). Canonicalizes the absolute target against the session project root and refuses strays — before the fact gate, independent of clearance. |
|
|
171
|
+
| Guard — careful + freeze together | Both of the above; they compose. Narrow the surface further with `CI_GATEGUARD_EXCLUDE`. |
|
|
172
|
+
|
|
173
|
+
If you previously opted into `safety-guard` for autonomous or production-adjacent runs, set `CI_GATEGUARD_TARGET_LOCK=block` for that session instead. That is a real refusal, not a checklist the agent can talk itself out of.
|
|
174
|
+
|
|
161
175
|
### Limitations and guarantees
|
|
162
176
|
|
|
163
177
|
- **Honor system.** Clearance is recorded by `ci_gateguard_clear`, the `gateguard-clear.mjs` CLI, a manual state-file write, or the inline `_gateguard_facts_presented` flag where the harness allows it (see "Clearing the gate" above). The hook can't verify the investigation actually happened; the 50-file cap — counted per session — bounds damage from stuck loops or rogue agents.
|
|
@@ -188,5 +202,6 @@ The standalone `gateguard-ai` Python/CLI package referenced in earlier drafts of
|
|
|
188
202
|
|
|
189
203
|
## Related Skills
|
|
190
204
|
|
|
191
|
-
- `safety
|
|
205
|
+
- `worktree-safety` — validates the worktree root before a source write; this skill gates the write itself
|
|
206
|
+
- `reconcile` — git ground truth before a destructive git action
|
|
192
207
|
- `code-reviewer` — Post-edit review (GateGuard is pre-edit investigation)
|
package/skills/reconcile.md
CHANGED
|
@@ -177,6 +177,5 @@ git branch -d <type>/<slug> # delete the merged feature branch (safe
|
|
|
177
177
|
|
|
178
178
|
- **`recall`** (Law 1) — before a risky git op, recall whether the same operation failed on this repo before.
|
|
179
179
|
- **`gateguard`** (Law 1) — the runtime gate (`hooks/gateguard.mjs`); `reconcile` is the procedure you run once a destructive git action is in play.
|
|
180
|
-
- **`safety-guard`** — destructive-operation guardrails for production and autonomous runs.
|
|
181
180
|
- **`audit`** (Law 4) — when an audit ends in a fix, `reconcile` is the safe path from branch to landed PR.
|
|
182
181
|
- **`commit-commands:commit-push-pr`** — the external-plugin equivalent of the commit → push → PR tail; `reconcile` reimplements it inline so the flow works with no companion installed. For a TDD-gated single-defect variant, use `/ship`.
|