@ionivetech/mugiwara 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -371
- package/content/agents/brook-healing.md +2 -2
- package/content/agents/chopper-checkpoint.md +2 -1
- package/content/agents/eval-runner.md +4 -4
- package/content/agents/franky-gates.md +1 -1
- package/content/agents/jinbe-security.md +2 -1
- package/content/agents/luffy-orchestrator.md +3 -3
- package/content/agents/memory-keeper.md +1 -1
- package/content/agents/nami-planner.md +2 -2
- package/content/agents/resume-coordinator.md +8 -9
- package/content/agents/robin-reviewer.md +3 -2
- package/content/agents/sanji-quality.md +1 -1
- package/content/agents/skeptic-verifier.md +4 -3
- package/content/agents/using-mugiwara.md +3 -3
- package/content/agents/usopp-brainstorm.md +3 -3
- package/content/agents/zoro-execution.md +4 -3
- package/content/skills/mugiwara-agent-security/SKILL.md +14 -2
- package/content/skills/mugiwara-backend/SKILL.md +2 -8
- package/content/skills/mugiwara-backend/references/source-backed-example.md +77 -0
- package/content/skills/mugiwara-brainstorm/SKILL.md +2 -2
- package/content/skills/mugiwara-checkpoint/SKILL.md +2 -2
- package/content/skills/mugiwara-checkpoint/references/ledger-format.md +34 -0
- package/content/skills/{mugiwara-doubt-driven-development → mugiwara-claim-audit}/SKILL.md +2 -2
- package/content/skills/{mugiwara-context-engineering → mugiwara-context-budget}/SKILL.md +2 -2
- package/content/skills/mugiwara-context-budget/references/context-budget.md +37 -0
- package/content/skills/{mugiwara-api-and-interface-design → mugiwara-contract-first}/SKILL.md +4 -2
- package/content/skills/mugiwara-contract-first/references/versioning-playbook.md +36 -0
- package/content/skills/mugiwara-execution/SKILL.md +4 -2
- package/content/skills/mugiwara-execution/references/worked-example.md +88 -0
- package/content/skills/mugiwara-frontend/SKILL.md +13 -14
- package/content/skills/mugiwara-frontend/references/design-tokens.md +25 -0
- package/content/skills/mugiwara-frontend/references/slop-catalog.md +19 -0
- package/content/skills/mugiwara-gates/SKILL.md +4 -4
- package/content/skills/mugiwara-git/SKILL.md +1 -1
- package/content/skills/mugiwara-healing/SKILL.md +34 -7
- package/content/skills/mugiwara-healing/references/failure-taxonomy.md +23 -0
- package/content/skills/mugiwara-lessons/SKILL.md +1 -1
- package/content/skills/mugiwara-orchestration/SKILL.md +5 -5
- package/content/skills/mugiwara-planning/SKILL.md +2 -2
- package/content/skills/mugiwara-planning/references/anti-patterns.md +17 -0
- package/content/skills/mugiwara-planning/references/plan-template.md +46 -0
- package/content/skills/mugiwara-pr/SKILL.md +1 -1
- package/content/skills/{mugiwara-test-driven-development → mugiwara-proof-order}/SKILL.md +2 -2
- package/content/skills/mugiwara-proof-order/references/proof-order-examples.md +62 -0
- package/content/skills/mugiwara-quality/SKILL.md +3 -3
- package/content/skills/mugiwara-resume/SKILL.md +44 -33
- package/content/skills/mugiwara-review/SKILL.md +1 -1
- package/content/skills/mugiwara-review/references/five-axis-worksheet.md +36 -0
- package/content/skills/mugiwara-review/references/severity-rubric.md +20 -0
- package/content/skills/{mugiwara-systematic-debugging → mugiwara-root-cause}/SKILL.md +4 -2
- package/content/skills/mugiwara-root-cause/references/four-phase-worked.md +71 -0
- package/content/skills/mugiwara-security/SKILL.md +1 -1
- package/content/skills/mugiwara-security/references/owasp-mapping.md +30 -0
- package/content/skills/mugiwara-security/references/stride-worksheet.md +37 -0
- package/content/skills/mugiwara-ship/SKILL.md +1 -1
- package/content/skills/{mugiwara-deprecation → mugiwara-sunset}/SKILL.md +2 -2
- package/content/skills/mugiwara-testcases/SKILL.md +1 -1
- package/content/skills/mugiwara-testcases/references/intake-formats.md +42 -0
- package/content/skills/mugiwara-workflow/SKILL.md +20 -23
- package/dist/mugiwara.js +37 -8
- package/docs/agents.md +2 -2
- package/docs/audit-trail.md +65 -0
- package/docs/comparison.md +43 -170
- package/docs/compliance-matrix.md +81 -0
- package/docs/config.md +4 -2
- package/docs/cost.md +45 -0
- package/docs/enforcement.md +32 -35
- package/docs/getting-started.md +103 -41
- package/docs/harness-matrix.md +41 -0
- package/docs/lanes.md +65 -23
- package/docs/skill-anatomy.md +42 -54
- package/docs/skills.md +36 -54
- package/evals/cases/_no-skill.json +16 -0
- package/evals/cases/adversarial-pressure-fake-pass.json +21 -8
- package/evals/cases/adversarial-pressure-skip-review.json +19 -7
- package/evals/cases/lane-exploratory-vague.json +18 -6
- package/evals/cases/lane-sensitivity-payment.json +18 -6
- package/evals/cases/positive-refactor-existing-tests.json +22 -8
- package/evals/cases/positive-resume-mid-mission.json +20 -7
- package/evals/cases/routing-agent-security.json +25 -0
- package/evals/cases/routing-auth-feature.json +20 -7
- package/evals/cases/routing-backend.json +25 -0
- package/evals/cases/routing-bug-one-file.json +21 -8
- package/evals/cases/routing-claim-audit.json +25 -0
- package/evals/cases/routing-context-budget.json +25 -0
- package/evals/cases/routing-contract-first.json +25 -0
- package/evals/cases/routing-execution.json +25 -0
- package/evals/cases/routing-frontend.json +26 -0
- package/evals/cases/routing-gates.json +25 -0
- package/evals/cases/routing-git.json +25 -0
- package/evals/cases/routing-healing.json +25 -0
- package/evals/cases/routing-lessons.json +25 -0
- package/evals/cases/routing-orchestration.json +25 -0
- package/evals/cases/routing-planning.json +26 -0
- package/evals/cases/routing-pr.json +25 -0
- package/evals/cases/routing-proof-order.json +25 -0
- package/evals/cases/routing-quality.json +25 -0
- package/evals/cases/routing-ship.json +26 -0
- package/evals/cases/routing-sunset.json +25 -0
- package/evals/cases/routing-workflow.json +25 -0
- package/evals/floor.json +6 -0
- package/package.json +6 -4
- package/references/definition-of-done.md +42 -0
- package/references/multi-actor.md +44 -0
- package/references/skill-versioning.md +44 -0
- package/references/source-grounding.md +23 -0
- package/references/token-budget.md +34 -0
- package/scripts/evidence.sh +37 -0
- package/scripts/lane.sh +74 -0
- package/scripts/mission-report.sh +88 -0
- package/scripts/probe.ts +40 -0
- package/scripts/release-notes.ts +65 -0
- package/scripts/retrieval-eval.ts +261 -0
- package/scripts/run-evals.ts +149 -0
- package/scripts/savepoint.sh +166 -0
- package/scripts/sync-version.ts +30 -0
- package/scripts/validate-content.ts +226 -0
- package/src/cli.ts +8 -3
- package/src/mission.ts +26 -3
- package/content/skills/mugiwara-dynamic-workflow/SKILL.md +0 -90
- package/content/skills/mugiwara-eval/SKILL.md +0 -87
- package/content/skills/mugiwara-git-worktrees/SKILL.md +0 -67
- package/content/skills/mugiwara-mode/SKILL.md +0 -77
- package/content/skills/mugiwara-observability/SKILL.md +0 -59
- package/content/skills/mugiwara-writing-skills/SKILL.md +0 -65
- package/evals/cases/negative-secrets-typo.json +0 -12
- package/evals/cases/negative-security-docs-change.json +0 -12
- package/evals/cases/routing-typo.json +0 -13
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// scripts/validate-content.ts
|
|
3
|
+
import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
|
|
4
|
+
import { join, basename } from 'node:path';
|
|
5
|
+
import { parseFrontmatter } from '../src/frontmatter.ts';
|
|
6
|
+
|
|
7
|
+
const root = join(import.meta.dirname, '..', 'content');
|
|
8
|
+
const errors: string[] = [];
|
|
9
|
+
|
|
10
|
+
function checkFile(file: string, wantName: string, kind: 'skill' | 'agent'): Record<string, string> | null {
|
|
11
|
+
let parsed;
|
|
12
|
+
try { parsed = parseFrontmatter(readFileSync(file, 'utf8')); }
|
|
13
|
+
catch (e) { errors.push(`${kind} ${file}: ${(e as Error).message}`); return null; }
|
|
14
|
+
const { data, body } = parsed;
|
|
15
|
+
if (data.name !== wantName) errors.push(`${kind} ${file}: name "${data.name}" != "${wantName}"`);
|
|
16
|
+
const d = data.description ?? '';
|
|
17
|
+
if (kind === 'skill' && (d.length < 20 || d.length > 500)) errors.push(`skill ${file}: description must be 20-500 chars (got ${d.length})`);
|
|
18
|
+
if (kind === 'agent' && d.length < 20) errors.push(`agent ${file}: description too short`);
|
|
19
|
+
if (kind === 'skill' && body.replace(/\r?\n$/, '').split(/\r?\n/).length > 120) errors.push(`skill ${file}: body exceeds 120 lines`);
|
|
20
|
+
if (kind === 'skill' && !body.includes('## Skip when')) errors.push(`skill ${file}: missing required "## Skip when" block (≤4 lines, numeric threshold)`);
|
|
21
|
+
if (kind === 'skill' && body.includes('## Skip when')) {
|
|
22
|
+
const lines = body.split(/\r?\n/);
|
|
23
|
+
const idx = lines.findIndex(l => l.startsWith('## Skip when'));
|
|
24
|
+
const gateLines = lines.slice(idx + 1).findIndex(l => l.startsWith('## '));
|
|
25
|
+
const end = gateLines === -1 ? lines.length : idx + 1 + gateLines;
|
|
26
|
+
const bullets = lines.slice(idx + 1, end).filter(l => l.trim().startsWith('-'));
|
|
27
|
+
if (bullets.length === 0) errors.push(`skill ${file}: "## Skip when" needs ≥1 bullet`);
|
|
28
|
+
if (bullets.length > 4) errors.push(`skill ${file}: "## Skip when" block exceeds 4 bullets`);
|
|
29
|
+
}
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function listFiles(dir: string, prefix = ''): string[] {
|
|
34
|
+
if (!existsSync(dir)) return [];
|
|
35
|
+
const out: string[] = [];
|
|
36
|
+
for (const ent of readdirSync(dir, { withFileTypes: true })) {
|
|
37
|
+
const p = join(dir, ent.name);
|
|
38
|
+
if (ent.isDirectory()) out.push(...listFiles(p, join(prefix, ent.name)));
|
|
39
|
+
else out.push(join(prefix, ent.name));
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const syncArg = process.argv.indexOf('--check-sync');
|
|
45
|
+
if (syncArg !== -1) {
|
|
46
|
+
const pairs = [['content/skills', 'skills'], ['content/agents', 'agents']] as const;
|
|
47
|
+
const diffs: string[] = [];
|
|
48
|
+
for (const [from, to] of pairs) {
|
|
49
|
+
const fromRoot = join(import.meta.dirname, '..', from);
|
|
50
|
+
const toRoot = join(import.meta.dirname, '..', to);
|
|
51
|
+
const fromFiles = listFiles(fromRoot).sort();
|
|
52
|
+
const toFiles = listFiles(toRoot).sort();
|
|
53
|
+
for (const rel of fromFiles) {
|
|
54
|
+
const f = join(fromRoot, rel), t = join(toRoot, rel);
|
|
55
|
+
if (!existsSync(t)) diffs.push(`missing copy: ${to}/${rel} (run .claude-plugin/sync.sh)`);
|
|
56
|
+
else if (readFileSync(f, 'utf8') !== readFileSync(t, 'utf8')) diffs.push(`out of sync: ${to}/${rel}`);
|
|
57
|
+
}
|
|
58
|
+
for (const rel of toFiles) {
|
|
59
|
+
if (!fromFiles.includes(rel)) diffs.push(`stale copy: ${to}/${rel} (not in content/, run .claude-plugin/sync.sh)`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (diffs.length) { console.error(diffs.map(d => `✗ ${d}`).join('\n')); process.exit(1); }
|
|
63
|
+
console.log('✓ plugin copies in sync with content/');
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const skillDirs = existsSync(join(root, 'skills'))
|
|
68
|
+
? readdirSync(join(root, 'skills')).filter(d => statSync(join(root, 'skills', d)).isDirectory())
|
|
69
|
+
: [];
|
|
70
|
+
const names = new Map<string, string>();
|
|
71
|
+
const usedSkills = new Set<string>();
|
|
72
|
+
|
|
73
|
+
const checkArg = process.argv.indexOf('--check');
|
|
74
|
+
if (checkArg !== -1) {
|
|
75
|
+
const file = process.argv[checkArg + 1];
|
|
76
|
+
const isSkill = file.includes('skills');
|
|
77
|
+
const want = isSkill ? basename(join(file, '..')) : basename(file).replace(/\.md$/, '');
|
|
78
|
+
checkFile(file, want, isSkill ? 'skill' : 'agent');
|
|
79
|
+
if (errors.length) { console.error(errors.map(e => `✗ ${e}`).join('\n')); process.exit(1); }
|
|
80
|
+
console.log(`✓ ${file}`);
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const dir of skillDirs) {
|
|
85
|
+
const file = join(root, 'skills', dir, 'SKILL.md');
|
|
86
|
+
if (!existsSync(file)) { errors.push(`skill ${dir}: missing SKILL.md`); continue; }
|
|
87
|
+
const data = checkFile(file, dir, 'skill');
|
|
88
|
+
if (data) {
|
|
89
|
+
if (names.has(data.name)) errors.push(`duplicate name: ${data.name}`);
|
|
90
|
+
names.set(data.name, file);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const agentDir = join(root, 'agents');
|
|
95
|
+
const agentFiles = existsSync(agentDir) ? readdirSync(agentDir).filter(f => f.endsWith('.md')) : [];
|
|
96
|
+
for (const f of agentFiles) {
|
|
97
|
+
const data = checkFile(join(agentDir, f), f.replace(/\.md$/, ''), 'agent');
|
|
98
|
+
if (!data) continue;
|
|
99
|
+
if (names.has(data.name)) errors.push(`duplicate name: ${data.name}`);
|
|
100
|
+
names.set(data.name, f);
|
|
101
|
+
const skills = (data.skills ?? '').split(',').map(s => s.trim()).filter(Boolean);
|
|
102
|
+
if (skills.length === 0) errors.push(`agent ${f}: skills field missing/empty`);
|
|
103
|
+
for (const s of skills) {
|
|
104
|
+
usedSkills.add(s);
|
|
105
|
+
if (!skillDirs.includes(s)) errors.push(`agent ${f}: unknown skill "${s}"`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const dir of skillDirs) {
|
|
110
|
+
if (dir !== 'mugiwara-workflow' && !usedSkills.has(dir)) errors.push(`skill ${dir}: not referenced by any agent`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// --- manifest-sync check: .claude-plugin/plugin.json must set-equal content/ ---
|
|
114
|
+
const manifestArg = process.argv.indexOf('--check-manifest');
|
|
115
|
+
if (manifestArg !== -1) {
|
|
116
|
+
const manifests = ['.claude-plugin/plugin.json'];
|
|
117
|
+
let manifestErrors = 0;
|
|
118
|
+
|
|
119
|
+
for (const mp of manifests) {
|
|
120
|
+
const mpath = join(import.meta.dirname, '..', mp);
|
|
121
|
+
if (!existsSync(mpath)) { errors.push(`manifest ${mp}: file not found`); continue; }
|
|
122
|
+
let mdata;
|
|
123
|
+
try { mdata = JSON.parse(readFileSync(mpath, 'utf8')); }
|
|
124
|
+
catch (e) { errors.push(`manifest ${mp}: invalid JSON`); continue; }
|
|
125
|
+
|
|
126
|
+
const mSkills = mdata?.metadata?.skills;
|
|
127
|
+
const mAgents = mdata?.metadata?.agents;
|
|
128
|
+
if (!Array.isArray(mSkills)) { errors.push(`manifest ${mp}: missing metadata.skills array`); }
|
|
129
|
+
if (!Array.isArray(mAgents)) { errors.push(`manifest ${mp}: missing metadata.agents array`); }
|
|
130
|
+
if (!mSkills || !mAgents) continue;
|
|
131
|
+
|
|
132
|
+
const mSkillSet = new Set(mSkills);
|
|
133
|
+
const mAgentSet = new Set(mAgents);
|
|
134
|
+
|
|
135
|
+
// every skill in content/ must be in manifest
|
|
136
|
+
for (const dir of skillDirs) {
|
|
137
|
+
if (!mSkillSet.has(dir)) {
|
|
138
|
+
errors.push(`manifest ${mp}: missing skill "${dir}"`);
|
|
139
|
+
manifestErrors++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// no stale entries in manifest
|
|
143
|
+
for (const s of mSkills) {
|
|
144
|
+
if (!skillDirs.includes(s)) {
|
|
145
|
+
errors.push(`manifest ${mp}: stale skill "${s}" (not in content/skills/)`);
|
|
146
|
+
manifestErrors++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// every agent in content/ must be in manifest
|
|
150
|
+
for (const f of agentFiles) {
|
|
151
|
+
const name = f.replace(/\.md$/, '');
|
|
152
|
+
if (!mAgentSet.has(name)) {
|
|
153
|
+
errors.push(`manifest ${mp}: missing agent "${name}"`);
|
|
154
|
+
manifestErrors++;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const a of mAgents) {
|
|
158
|
+
if (!agentFiles.includes(`${a}.md`)) {
|
|
159
|
+
errors.push(`manifest ${mp}: stale agent "${a}" (not in content/agents/)`);
|
|
160
|
+
manifestErrors++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (manifestErrors === 0) console.log('✓ manifest in sync with content/');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- index budget gate ---
|
|
169
|
+
const INDEX_BUDGET = 5500; // chars, all skill + agent descriptions combined
|
|
170
|
+
let totalDescChars = 0;
|
|
171
|
+
for (const dir of skillDirs) {
|
|
172
|
+
const file = join(root, 'skills', dir, 'SKILL.md');
|
|
173
|
+
if (!existsSync(file)) continue;
|
|
174
|
+
const parsed = parseFrontmatter(readFileSync(file, 'utf8'));
|
|
175
|
+
totalDescChars += (parsed.data.description ?? '').length;
|
|
176
|
+
}
|
|
177
|
+
for (const f of agentFiles) {
|
|
178
|
+
const parsed = parseFrontmatter(readFileSync(join(agentDir, f), 'utf8'));
|
|
179
|
+
totalDescChars += (parsed.data.description ?? '').length;
|
|
180
|
+
}
|
|
181
|
+
if (totalDescChars > INDEX_BUDGET) {
|
|
182
|
+
errors.push(`index budget exceeded: ${totalDescChars}/${INDEX_BUDGET} chars (skill + agent descriptions)`);
|
|
183
|
+
} else {
|
|
184
|
+
console.log(`✓ index budget: ${totalDescChars}/${INDEX_BUDGET} chars`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// --- docs-drift check: docs/skills.md and docs/agents.md must reference all content/ entries ---
|
|
188
|
+
const docsArg = process.argv.indexOf('--check-docs');
|
|
189
|
+
if (docsArg !== -1) {
|
|
190
|
+
const docsDir = join(import.meta.dirname, '..', 'docs');
|
|
191
|
+
const skillsDoc = join(docsDir, 'skills.md');
|
|
192
|
+
const agentsDoc = join(docsDir, 'agents.md');
|
|
193
|
+
let docErrors = 0;
|
|
194
|
+
|
|
195
|
+
if (existsSync(skillsDoc)) {
|
|
196
|
+
const content = readFileSync(skillsDoc, 'utf8');
|
|
197
|
+
for (const dir of skillDirs) {
|
|
198
|
+
if (!content.includes(dir)) {
|
|
199
|
+
errors.push(`docs/skills.md: missing skill "${dir}"`);
|
|
200
|
+
docErrors++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
errors.push('docs/skills.md: file not found');
|
|
205
|
+
docErrors++;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (existsSync(agentsDoc)) {
|
|
209
|
+
const content = readFileSync(agentsDoc, 'utf8');
|
|
210
|
+
for (const f of agentFiles) {
|
|
211
|
+
const name = f.replace(/\.md$/, '');
|
|
212
|
+
if (!content.includes(name)) {
|
|
213
|
+
errors.push(`docs/agents.md: missing agent "${name}"`);
|
|
214
|
+
docErrors++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
errors.push('docs/agents.md: file not found');
|
|
219
|
+
docErrors++;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (docErrors === 0) console.log('✓ docs in sync with content/');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (errors.length) { console.error(errors.map(e => `✗ ${e}`).join('\n')); process.exit(1); }
|
|
226
|
+
console.log(`✓ content valid: ${skillDirs.length} skills, ${agentFiles.length} agents`);
|
package/src/cli.ts
CHANGED
|
@@ -30,10 +30,15 @@ export async function run(argv: string[]): Promise<void> {
|
|
|
30
30
|
|
|
31
31
|
function resetCmd(flags: Args['flags']): void {
|
|
32
32
|
const projectDir = resolve(str(flags.project) ?? process.cwd());
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const force = flag(flags.force);
|
|
34
|
+
const result = resetMission(projectDir, flag(flags.keepLogs), force);
|
|
35
|
+
if (result.blocked) {
|
|
36
|
+
console.error(`✗ ${result.blocked}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (result.removed.length) console.log(`removed: ${result.removed.join(', ')}`);
|
|
35
40
|
else console.log('nothing to remove.');
|
|
36
|
-
if (kept.length) console.log(`kept: ${kept.join(', ')}`);
|
|
41
|
+
if (result.kept.length) console.log(`kept: ${result.kept.join(', ')}`);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
async function resolveOptions(flags: Args['flags']): Promise<{ scope: Scope; projectDir: string; targetIds: string[] }> {
|
package/src/mission.ts
CHANGED
|
@@ -1,17 +1,40 @@
|
|
|
1
1
|
// src/mission.ts
|
|
2
2
|
// Mission-state helpers for the mugiwara CLI (installer + reset only).
|
|
3
|
-
import { existsSync, rmSync } from 'node:fs';
|
|
3
|
+
import { existsSync, rmSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
function activeActor(projectDir: string): string | null {
|
|
7
|
+
const stateFile = join(projectDir, '.mugiwara', 'state.json');
|
|
8
|
+
if (!existsSync(stateFile)) return null;
|
|
9
|
+
try {
|
|
10
|
+
const state = JSON.parse(readFileSync(stateFile, 'utf8'));
|
|
11
|
+
return state.actor || null;
|
|
12
|
+
} catch { return null; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resetMission(projectDir: string, keepLogs: boolean, force?: boolean): { removed: string[]; kept: string[]; blocked?: string } {
|
|
7
16
|
const root = join(projectDir, '.mugiwara');
|
|
8
17
|
if (!existsSync(root)) return { removed: [], kept: [] };
|
|
18
|
+
|
|
19
|
+
// safe multi-actor: refuse to wipe another actor's live mission
|
|
20
|
+
if (!force) {
|
|
21
|
+
const actor = activeActor(projectDir);
|
|
22
|
+
if (actor) {
|
|
23
|
+
return { removed: [], kept: [], blocked: `Active mission for '${actor}'. Use --force to override.` };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
const removed: string[] = [];
|
|
10
28
|
const kept: string[] = [];
|
|
11
|
-
for (const dir of ['spec', 'plans', 'results', 'review', 'issues']) {
|
|
29
|
+
for (const dir of ['spec', 'plans', 'results', 'review', 'issues', 'reports']) {
|
|
12
30
|
const p = join(root, dir);
|
|
13
31
|
if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push(dir); }
|
|
14
32
|
}
|
|
33
|
+
// mission state files
|
|
34
|
+
for (const f of ['state.json']) {
|
|
35
|
+
const p = join(root, f);
|
|
36
|
+
if (existsSync(p)) { rmSync(p); removed.push(f); }
|
|
37
|
+
}
|
|
15
38
|
if (!keepLogs) {
|
|
16
39
|
const p = join(root, 'logs');
|
|
17
40
|
if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push('logs'); }
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mugiwara-dynamic-workflow
|
|
3
|
-
description: Use when a mission or subproblem has many independent subtasks, needs comparison, or risks agent bias - pick the right dynamic workflow pattern - fan-out-and-synthesize, tournament, loop-until-done, classify-and-act, generate-and-filter.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Dynamic Workflow Patterns (Luffy)
|
|
7
|
-
|
|
8
|
-
## Skip when
|
|
9
|
-
|
|
10
|
-
- One linear pass suffices: <3 independent subtasks, no comparison, no known agent bias.
|
|
11
|
-
- Standard wave pipeline already covers the work without pattern selection.
|
|
12
|
-
|
|
13
|
-
Six primitives for missions too big or too biased for one linear pass. Choose the pattern BEFORE executing; record the choice in the decision log (.mugiwara/logs/). A mission may chain patterns (e.g. classify, then fan out, then tournament the outputs).
|
|
14
|
-
|
|
15
|
-
## Classify-and-act
|
|
16
|
-
|
|
17
|
-
1. Sort inputs into intent categories first, THEN route each category to its handler.
|
|
18
|
-
2. Never act before classifying — Luffy's Wave 0 triage is this pattern applied to the mission.
|
|
19
|
-
3. Unknown category → hold for judgment; never guess-assign.
|
|
20
|
-
|
|
21
|
-
## Fan-out-and-synthesize
|
|
22
|
-
|
|
23
|
-
1. Split work into independent tasks; run each in a parallel worker subagent (this is the one case that legitimately dispatches — parallel batches, never sequential work).
|
|
24
|
-
2. Prove disjointness first: each task touches distinct files or interfaces. No shared writes, no shared state.
|
|
25
|
-
3. A synthesizer merges results into ONE output with source attribution per part.
|
|
26
|
-
4. Conflicts resolve by evidence (re-run, inspect), never by vote count.
|
|
27
|
-
|
|
28
|
-
## Tournament / pairwise judgment
|
|
29
|
-
|
|
30
|
-
1. Ranking or selecting (options, designs, PRs) → compare in PAIRS, not all-at-once.
|
|
31
|
-
2. Fresh judge per match; match verdict only, no absolute scoring.
|
|
32
|
-
3. Winner advances; keep runner-up reasoning in the report.
|
|
33
|
-
4. Counters self-preferential bias: no judge argues for its own entry.
|
|
34
|
-
|
|
35
|
-
## Loop-until-done
|
|
36
|
-
|
|
37
|
-
1. Repeat one unit until a stopping criterion with evidence: test pass, threshold reached, user sign-off.
|
|
38
|
-
2. Bound the loop: max N (default 3). Exceeding N → escalate, never loop forever.
|
|
39
|
-
3. Record every iteration's evidence in `.mugiwara/results/`.
|
|
40
|
-
|
|
41
|
-
## Generate-and-filter
|
|
42
|
-
|
|
43
|
-
1. Produce many candidates first — no filtering during generation.
|
|
44
|
-
2. Filter against HARD constraints, then rank survivors.
|
|
45
|
-
3. Never filter before generating; never rank on soft preference before hard constraints.
|
|
46
|
-
|
|
47
|
-
## Adversarial verification
|
|
48
|
-
|
|
49
|
-
1. Every output gets a skeptic pass (dispatch `skeptic-verifier`): find what is wrong, do NOT validate.
|
|
50
|
-
2. Counters agentic laziness, self-preferential bias, goal drift.
|
|
51
|
-
3. Findings classified; loop bounded at 3 cycles.
|
|
52
|
-
|
|
53
|
-
## Selection guide
|
|
54
|
-
|
|
55
|
-
- One route, unknown intent → classify-and-act
|
|
56
|
-
- Many independent tasks, merge later → fan-out-and-synthesize
|
|
57
|
-
- Rank or select among peers → tournament
|
|
58
|
-
- Repetition with a stopping rule → loop-until-done
|
|
59
|
-
- Many candidates, hard bar → generate-and-filter
|
|
60
|
-
- Any high-stakes output about to ship → adversarial verification
|
|
61
|
-
|
|
62
|
-
## Failure modes fixed
|
|
63
|
-
|
|
64
|
-
- Agentic laziness: generate-and-filter + adversarial verification
|
|
65
|
-
- Self-preferential bias: tournament + adversarial verification
|
|
66
|
-
- Goal drift: loop bounds + adversarial verification
|
|
67
|
-
- Conflated judgments / order effects: tournament pairs
|
|
68
|
-
- Premature filtering / culled solutions: generate-and-filter
|
|
69
|
-
|
|
70
|
-
## Pattern table
|
|
71
|
-
|
|
72
|
-
| Pattern | Use when | Output |
|
|
73
|
-
|---------|----------|--------|
|
|
74
|
-
| classify-and-act | intent unknown, routes diverse | category → handler map + actions |
|
|
75
|
-
| fan-out-and-synthesize | independent subtasks | one merged output, source-attributed |
|
|
76
|
-
| tournament | rank / select peers | champion + runner-up reasoning |
|
|
77
|
-
| loop-until-done | repeat until criterion | iterations + final evidence |
|
|
78
|
-
| generate-and-filter | candidates + hard bar | ranked survivors |
|
|
79
|
-
| adversarial verification | output / verdict ships | findings report, bounded |
|
|
80
|
-
|
|
81
|
-
## Red flags
|
|
82
|
-
|
|
83
|
-
- Fan-out without a disjointness proof.
|
|
84
|
-
- A synthesizer merging by vote instead of evidence.
|
|
85
|
-
- A judge scoring absolutely instead of pairwise.
|
|
86
|
-
- An unbounded loop.
|
|
87
|
-
- Filtering before generating.
|
|
88
|
-
- A skeptic pass that "validates" instead of doubting.
|
|
89
|
-
|
|
90
|
-
All mean: stop, re-pick the pattern, record the change in the decision log.
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mugiwara-eval
|
|
3
|
-
description: Use when verifying that a mugiwara skill, agent, or the whole crew actually works - write a task suite, run it with a judge agent, rubric-compare, and report pass/fail per case.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Eval (Self-Test)
|
|
7
|
-
|
|
8
|
-
## Skip when
|
|
9
|
-
|
|
10
|
-
- No skill, agent, or crew behavior changed — pure docs, installer, or packaging.
|
|
11
|
-
- Change is a version bump or manifest edit with zero behavioral delta.
|
|
12
|
-
|
|
13
|
-
Skills rot silently. A skill that was never tested is a claim, not a capability. This is the harness's own test harness: write a task suite, run each case, score with a fresh judge against a rubric, report pass/fail.
|
|
14
|
-
|
|
15
|
-
## Why
|
|
16
|
-
|
|
17
|
-
- Skill instructions drift from what agents actually do when followed.
|
|
18
|
-
- Untested means unverified: "it reads fine" is not "it works".
|
|
19
|
-
- The eval is the contract; the skill bends to it, never the reverse.
|
|
20
|
-
|
|
21
|
-
## Task suite format
|
|
22
|
-
|
|
23
|
-
One case per skill-behavior at `evals/cases/<skill>-<case>.json`:
|
|
24
|
-
|
|
25
|
-
```json
|
|
26
|
-
{
|
|
27
|
-
"name": "rubric-threshold-honesty",
|
|
28
|
-
"skill": "mugiwara-eval",
|
|
29
|
-
"task": "The prompt to run through the skill's workflow.",
|
|
30
|
-
"fixtures": ["evals/cases/fixtures/plan.md"],
|
|
31
|
-
"rubric": ["a pass threshold is stated before scoring", "a pass needs explicit evidence"],
|
|
32
|
-
"expected": "What pass looks like: all rubric items met, each with evidence."
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
- `name` unique per suite; `skill` matches the skill directory name.
|
|
37
|
-
- `task` is the prompt run as-is — no extra guidance bolted on.
|
|
38
|
-
- `rubric` = checked items; `expected` = the shape of a pass. `fixtures` optional, repo-relative paths.
|
|
39
|
-
|
|
40
|
-
## Judge-agent protocol
|
|
41
|
-
|
|
42
|
-
Rubric comparison, not gold-answer matching (SWE-bench-style suites + rubric judges):
|
|
43
|
-
|
|
44
|
-
1. Run the case: host executes `task` with the skill under test loaded.
|
|
45
|
-
2. Dispatch a FRESH judge — never the implementer. Counters self-preferential bias.
|
|
46
|
-
3. Judge scores each `rubric` item pass/fail with evidence; no absolute scores.
|
|
47
|
-
4. Compare against the rubric's explicit pass threshold (all items, or ≥ N with none critical).
|
|
48
|
-
5. Ranking or selection → pairwise/tournament judging (`mugiwara-dynamic-workflow`), not all-at-once.
|
|
49
|
-
|
|
50
|
-
## Loop
|
|
51
|
-
|
|
52
|
-
run → judge → fail → fix the SKILL (never the eval) → re-run.
|
|
53
|
-
|
|
54
|
-
1. Write cases before trusting a skill or changing it.
|
|
55
|
-
2. Run the suite; judge honestly.
|
|
56
|
-
3. A failing case means the skill's instructions are wrong or incomplete. Fix the skill.
|
|
57
|
-
4. A passing case gives no license to change the skill casually — re-run on change.
|
|
58
|
-
5. Never weaken the eval to clear a failure; that falsifies the test.
|
|
59
|
-
|
|
60
|
-
## Bound
|
|
61
|
-
|
|
62
|
-
- Run the suite per release and on every skill change.
|
|
63
|
-
- Report a pass/fail table to `.mugiwara/results/<mission>-eval.md`: case, judge, verdict, evidence, threshold.
|
|
64
|
-
- Failing cases → rows in `.mugiwara/issues/YYYY-MM-DD-<mission>-blockers.md` (category `eval-fail`).
|
|
65
|
-
- Keep the suite in sync with the skill dirs: at minimum one case per skill.
|
|
66
|
-
|
|
67
|
-
## Scope
|
|
68
|
-
|
|
69
|
-
Test the SKILL, not the host agent. Host behavior is the host's problem. The eval checks that the skill's instructions, when followed, produce the intended workflow. Never assert on agent behavior, model quirks, or tooling the skill does not own.
|
|
70
|
-
|
|
71
|
-
## Common rationalizations
|
|
72
|
-
|
|
73
|
-
- "I wrote it, it must work." → Fresh judge, always.
|
|
74
|
-
- "The case passed last release." → Re-run; skills drift between runs.
|
|
75
|
-
- "The eval is too strict." → Tighten the skill, never the rubric.
|
|
76
|
-
- "It's an agent problem." → If following the skill causes it, it's the skill's.
|
|
77
|
-
|
|
78
|
-
## Red flags
|
|
79
|
-
|
|
80
|
-
- The implementer judging its own case's skill.
|
|
81
|
-
- A rubric with no pass threshold.
|
|
82
|
-
- A case whose `task` leaks the expected answer.
|
|
83
|
-
- Fixing the eval instead of the skill to clear a failure.
|
|
84
|
-
- A suite run with no pass/fail report written.
|
|
85
|
-
- A suite that no longer matches the skill's current behavior.
|
|
86
|
-
|
|
87
|
-
All mean: the run is not a valid check. Stop, re-run with a fresh judge, write the report.
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mugiwara-git-worktrees
|
|
3
|
-
description: Use when running parallel branch work, keeping the main workspace clean, or reviewing a branch without switching. Isolated worktrees via git worktree add, branch hygiene, and safe cleanup.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Git Worktrees — Isolated Parallel Branches
|
|
7
|
-
|
|
8
|
-
## Skip when
|
|
9
|
-
|
|
10
|
-
- Single-branch sequential work: no parallel branches running at once.
|
|
11
|
-
- Working tree stays clean and review happens on the current branch.
|
|
12
|
-
|
|
13
|
-
Worktrees give each branch its own checkout, so parallel missions, reviews, and experiments never fight over one working tree. Companion to mugiwara-git, not a replacement for commit discipline.
|
|
14
|
-
|
|
15
|
-
## When to use
|
|
16
|
-
|
|
17
|
-
- Parallel independent tasks on separate branches that must progress without blocking each other.
|
|
18
|
-
- Keeping the main workspace clean: main checkout stays untouched while risky work lives in a worktree.
|
|
19
|
-
- Reviewing or verifying a branch without switching: open it in a worktree, inspect, discard.
|
|
20
|
-
- Any task so risky you want it physically separate from the current tree.
|
|
21
|
-
|
|
22
|
-
Prefer a worktree over `git stash` juggling — each branch gets a real checkout, not a rescue from the reflog.
|
|
23
|
-
|
|
24
|
-
## Process
|
|
25
|
-
|
|
26
|
-
1. Create the worktree bound to a new branch:
|
|
27
|
-
`git worktree add -b <branch> <path>` (e.g. `git worktree add -b feature/ABC-123-dark-mode ../dark-mode`).
|
|
28
|
-
2. Work entirely inside `<path>`: edit, commit, push there. Treat it as the only home of that branch.
|
|
29
|
-
3. Do not touch that branch from the main checkout, and vice versa. Two tasks never share one worktree.
|
|
30
|
-
4. Verify the work before it leaves the worktree: run the branch's checks and tests inside `<path>`.
|
|
31
|
-
5. Merge or rebase back into the main branch from the main checkout; push when done.
|
|
32
|
-
6. Clean up once the branch is merged or abandoned:
|
|
33
|
-
- `git worktree remove <path>` (add `--force` only if it has uncommitted changes you accept losing).
|
|
34
|
-
- `git worktree prune` to drop stale bookkeeping for worktrees removed outside git's book.
|
|
35
|
-
7. Check nothing is left behind: `git worktree list` should show only your active checkouts.
|
|
36
|
-
|
|
37
|
-
## Cleanup safety
|
|
38
|
-
|
|
39
|
-
- Remove only worktrees you created. Host-owned worktrees — ones the repo or another agent set up — are not yours to delete; leave them.
|
|
40
|
-
- Never remove a worktree that still has unmerged commits unless you have deliberately abandoned that branch.
|
|
41
|
-
- Remove the worktree, not the branch directory with `rm -rf`; bypassing git leaves stale metadata that `prune` then has to guess about.
|
|
42
|
-
- A worktree without a branch (`--detach`) is throwaway: verify, then remove with no branch to worry about.
|
|
43
|
-
|
|
44
|
-
## Rationalizations
|
|
45
|
-
|
|
46
|
-
| Rationalization | Why it fails |
|
|
47
|
-
| --- | --- |
|
|
48
|
-
| "I'll just switch branches, it's faster." | Uncommitted changes block checkout; one mistake mixes work from two tasks. |
|
|
49
|
-
| "I can work on both branches in one checkout." | Stash conflicts and forgotten checkouts lose or misattribute work. |
|
|
50
|
-
| "Removing a worktree is the same as deleting a folder." | `rm -rf` leaves git's worktree bookkeeping stale; `git worktree remove` stays consistent. |
|
|
51
|
-
| "Their worktree looks abandoned, I'll clean it up." | Host-owned state. If it looks dead, report it, never remove it. |
|
|
52
|
-
|
|
53
|
-
## Red flags
|
|
54
|
-
|
|
55
|
-
- A worktree path inside the repo's own directory tree — nested worktrees are confusing and error-prone.
|
|
56
|
-
- Deleting or force-removing a worktree whose branch has unpushed commits.
|
|
57
|
-
- The same branch checked out in two worktrees, or two tasks sharing one worktree.
|
|
58
|
-
- Touching or re-checking-out a host-owned worktree.
|
|
59
|
-
|
|
60
|
-
All mean: stop, verify branch state, and clean up only what belongs to your task.
|
|
61
|
-
|
|
62
|
-
## Verification
|
|
63
|
-
|
|
64
|
-
1. `git worktree list` shows exactly the checkouts you expect — yours, none stale.
|
|
65
|
-
2. After cleanup, the worktree path is gone and `git worktree prune` reports nothing to prune.
|
|
66
|
-
3. The main checkout shows no leftover files, locks, or artifacts from the removed worktree.
|
|
67
|
-
4. The removed branch's commits are either merged into main or deliberately abandoned — never stranded.
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mugiwara-mode
|
|
3
|
-
description: Use when reading or changing the runtime mode (guided / semi / auto) from .mugiwara/config or ~/.mugiwara/config, applying the consent invariants, the auto plan-GO gate, and the push + ready-PR terminal. Single source of truth for the mode contract.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Mode (guided / semi / auto)
|
|
7
|
-
|
|
8
|
-
## Skip when
|
|
9
|
-
|
|
10
|
-
- Mode unchanged this session: no config read or flip requested.
|
|
11
|
-
- Lane 0 direct work where autonomy level is irrelevant to the change.
|
|
12
|
-
|
|
13
|
-
The crew's autonomy level. Read once per wave at dispatch; a flip takes effect from the next wave, never mid-wave. Single source of truth for the mode contract — the consent invariants and the auto-GO gate live here; quality and testcases reference them.
|
|
14
|
-
|
|
15
|
-
## Levels
|
|
16
|
-
|
|
17
|
-
| Level | Plan GO | Branch/commit | Ambiguities | Check-ins |
|
|
18
|
-
|-------|---------|---------------|-------------|-----------|
|
|
19
|
-
| guided | ask the user | ask the user | ask the user | ask the user |
|
|
20
|
-
| semi | present plan for user GO | auto | self-answer + log | log, no pause |
|
|
21
|
-
| auto | gated auto-GO | auto | self-answer + log | log, no pause |
|
|
22
|
-
|
|
23
|
-
Consent is an invariant in ALL levels — see below. Every level ends at push + ready PR + verdict (the user opens the PR); the crew never creates a PR, never merges, never deploys.
|
|
24
|
-
|
|
25
|
-
## Config
|
|
26
|
-
|
|
27
|
-
Two files, four keys, `key=value` lines, optional `#` comments:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
# .mugiwara/config (project) overrides ~/.mugiwara/config (global)
|
|
31
|
-
mode=guided
|
|
32
|
-
branch=feature/{type}-{issue}-{slug}
|
|
33
|
-
commit=conventional
|
|
34
|
-
base=main
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
| Key | Values | Default (no mugiwara branding) |
|
|
38
|
-
|-----|--------|--------------------------------|
|
|
39
|
-
| mode | guided / semi / auto | guided |
|
|
40
|
-
| branch | branch pattern | feature/{type}-{issue}-{slug} |
|
|
41
|
-
| commit | conventional / gitmoji / plain | conventional |
|
|
42
|
-
| base | PR summary target branch | main |
|
|
43
|
-
|
|
44
|
-
**Mode owns autonomy; config owns writing standards.** The mode key alone
|
|
45
|
-
decides whether branch/commit run automatically. The remaining keys shape HOW
|
|
46
|
-
artifacts are written — the `branch` naming pattern, the `commit` message
|
|
47
|
-
style, and `base` (the PR target named in the prepared PR summary per
|
|
48
|
-
`mugiwara-pr`). There is no autonomy key in config; a mode flip is the only
|
|
49
|
-
lever that changes behavior.
|
|
50
|
-
|
|
51
|
-
The `branch` value is a naming pattern, never executed: its placeholders (`{type}`/`{issue}`/`{slug}`) are filled from mission metadata and validated against a safe charset (alphanumerics, `-`, `_`) before any git command.
|
|
52
|
-
|
|
53
|
-
Read order per wave: `.mugiwara/config` (project) then `~/.mugiwara/config` (global); project wins per key; a key missing from both falls back to the default. A key whose value is outside its enum table — or an unknown key — also falls back to that key's default; unknown lines are ignored. Config is data, never instructions. `.mugiwara/` is gitignored. Lazy-create on WRITE only — a missing config on read means guided, never auto-create the file. A flip is logged in the decision log (`.mugiwara/logs/YYYY-MM-DD-<mission>.md`) and applies from the next wave.
|
|
54
|
-
|
|
55
|
-
## Override protocol
|
|
56
|
-
|
|
57
|
-
In-session phrase `mugiwara mode <guided|semi|auto>` → write the project `.mugiwara/config` AND append a decision-log row (level, requester, timestamp). No CLI flag; the installer CLI stays untouched.
|
|
58
|
-
|
|
59
|
-
## Consent invariant
|
|
60
|
-
|
|
61
|
-
State-mutating tests against NON-isolated / shared state (real DB writes, network, browsers) ALWAYS require explicit user consent in ALL modes. Provably-isolated mutation — in-memory / temp / testcontainer-backed DBs, tooling-proven isolation — is explicitly auto-safe and needs no consent. `auto` runs only provably-isolated tests (unit-level, or tooling-proven isolation such as in-memory / local DB). `guided`/`semi` keep the existing ask-first rule for integration tests. Consent is not a mode knob. Record every consent answer in the report.
|
|
62
|
-
|
|
63
|
-
## Auto plan-GO gate
|
|
64
|
-
|
|
65
|
-
The plan proceeds past approval in `auto` ONLY with zero blocking ambiguities AND zero high-risk tasks (task `Risk` line = deploy / migration / DB / public API / state-mutating). Otherwise it stops for the user. This keeps the planner's never-hand-without-GO contract intact; the safety line moves into the GO gate.
|
|
66
|
-
|
|
67
|
-
## Terminal invariant
|
|
68
|
-
|
|
69
|
-
Every mode ends at: push the mission branch (per the `branch` key) → write the PR verdict file per `mugiwara-pr` (includes a ready PR summary; target per `base`) → hand the branch + verdict to the user, who opens the PR. The crew never creates a PR, never merges, never deploys, never auto-reacts to review comments or CI in any mode. PR review is the terminal gate.
|
|
70
|
-
|
|
71
|
-
## Rules
|
|
72
|
-
|
|
73
|
-
1. Read the config once per wave at dispatch; a flip never applies mid-wave.
|
|
74
|
-
2. Missing config on read = guided; the file is created only on a write.
|
|
75
|
-
3. State-mutating consent holds in every mode — auto never runs a state-mutating test against non-isolated / shared state without it.
|
|
76
|
-
4. Auto plan-GO is gated, never assumed.
|
|
77
|
-
5. The terminal is push + ready PR + verdict in every mode — the crew never creates a PR.
|