harness-bujang 0.5.9 → 0.6.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/dist/{chunk-FE2GLGVF.js → chunk-6LROBVZM.js} +144 -5
- package/dist/index.js +20 -3
- package/dist/{init-S3YF6RT4.js → init-4G7R63DX.js} +1 -1
- package/dist/{update-4WS42LXD.js → update-KHDRQZQE.js} +1 -1
- package/package.json +1 -1
- package/templates/agents/en/cofounder.md +8 -0
- package/templates/agents/en/director.md +11 -1
- package/templates/agents/ko/cofounder.md +8 -0
- package/templates/agents/ko/director.md +11 -1
- package/templates/templates/en/CLAUDE.md.harness-section.template +17 -6
- package/templates/templates/ko/CLAUDE.md.harness-section.template +17 -6
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import * as fs2 from "fs/promises";
|
|
7
7
|
import * as path2 from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
|
-
import { select, confirm } from "@inquirer/prompts";
|
|
9
|
+
import { select, confirm, checkbox } from "@inquirer/prompts";
|
|
10
10
|
|
|
11
11
|
// src/scan.ts
|
|
12
12
|
import * as fs from "fs/promises";
|
|
@@ -169,6 +169,27 @@ async function resolveAssetPaths() {
|
|
|
169
169
|
If installed via npm, try reinstalling. If running from source, run "npm run build" in packages/cli first.`
|
|
170
170
|
);
|
|
171
171
|
}
|
|
172
|
+
var ALL_ADAPTERS = ["cursor", "cline", "aider", "codex", "gemini"];
|
|
173
|
+
var BALANCED_MAPPING = {
|
|
174
|
+
director: "opus",
|
|
175
|
+
cofounder: "opus",
|
|
176
|
+
"architect-team": "opus",
|
|
177
|
+
consultant: "opus",
|
|
178
|
+
"security-team": "opus",
|
|
179
|
+
"db-guard-team": "opus",
|
|
180
|
+
"dev-team": "sonnet",
|
|
181
|
+
"code-review-team": "sonnet",
|
|
182
|
+
"qa-team": "sonnet",
|
|
183
|
+
"doc-sync-team": "sonnet",
|
|
184
|
+
"research-team": "sonnet",
|
|
185
|
+
"analysis-team": "sonnet",
|
|
186
|
+
"script-team": "sonnet",
|
|
187
|
+
"verifier-team": "haiku",
|
|
188
|
+
"image-team": "haiku",
|
|
189
|
+
"voice-team": "haiku",
|
|
190
|
+
"edit-team": "haiku",
|
|
191
|
+
"content-qa-team": "haiku"
|
|
192
|
+
};
|
|
172
193
|
async function runInit(args) {
|
|
173
194
|
let opts = parseArgs(args);
|
|
174
195
|
const assets = await resolveAssetPaths();
|
|
@@ -211,6 +232,8 @@ async function runInit(args) {
|
|
|
211
232
|
console.log(c.bold("\u{1F4CB} Configuration"));
|
|
212
233
|
console.log(c.dim(` Language: ${opts.lang}`));
|
|
213
234
|
console.log(c.dim(` Chat backend: ${opts.chatBackend}${opts.chatBackend === "sqlite" ? " (local file)" : " (cloud Postgres)"}`));
|
|
235
|
+
console.log(c.dim(` Tools: claude${opts.adapters.length > 0 ? ` + ${opts.adapters.join(", ")}` : " (only)"}`));
|
|
236
|
+
console.log(c.dim(` Models: ${describeModelMap(opts.modelMap)}`));
|
|
214
237
|
if (scan.framework.startsWith("Next.js")) {
|
|
215
238
|
console.log(c.dim(` Chat-room UI: ${opts.installTemplate ? "install" : "skip"}`));
|
|
216
239
|
}
|
|
@@ -287,8 +310,12 @@ async function runInit(args) {
|
|
|
287
310
|
continue;
|
|
288
311
|
}
|
|
289
312
|
const raw = await fs2.readFile(path2.join(agentsSrc, f), "utf8");
|
|
290
|
-
|
|
291
|
-
|
|
313
|
+
const slug = f.replace(/\.md$/, "");
|
|
314
|
+
const override = opts.modelMap[slug];
|
|
315
|
+
const rendered = renderTemplate(raw, context);
|
|
316
|
+
const final = override ? overrideModelFrontmatter(rendered, override) : rendered;
|
|
317
|
+
await fs2.writeFile(dst, final);
|
|
318
|
+
console.log(` ${c.green("\u2713")} ${f}${override ? c.dim(` \u2192 model: ${override}`) : ""}`);
|
|
292
319
|
}
|
|
293
320
|
console.log();
|
|
294
321
|
if (opts.editClaudeMd) {
|
|
@@ -380,6 +407,16 @@ async function runInit(args) {
|
|
|
380
407
|
console.log();
|
|
381
408
|
}
|
|
382
409
|
}
|
|
410
|
+
if (opts.adapters.length > 0) {
|
|
411
|
+
console.log(c.bold("\u{1F501} Fanning out to extra tool adapters"));
|
|
412
|
+
console.log(c.dim(` Targets: ${opts.adapters.join(", ")}`));
|
|
413
|
+
const { runAdapt } = await import("./adapt-VPWOYF6W.js");
|
|
414
|
+
await runAdapt([
|
|
415
|
+
`--to=${opts.adapters.join(",")}`,
|
|
416
|
+
`--target=${opts.target}`,
|
|
417
|
+
"--yes"
|
|
418
|
+
]);
|
|
419
|
+
}
|
|
383
420
|
console.log(c.bold(c.green("\u2705 Done.")));
|
|
384
421
|
console.log();
|
|
385
422
|
console.log("Next steps:");
|
|
@@ -431,6 +468,35 @@ async function promptInteractive(opts, scan) {
|
|
|
431
468
|
],
|
|
432
469
|
default: opts.chatBackend
|
|
433
470
|
});
|
|
471
|
+
const isPreset = (t) => opts.adapters.includes(t);
|
|
472
|
+
const adapters = await checkbox({
|
|
473
|
+
message: "Extra tool adapters? (Claude Code is always installed at .claude/agents/ \u2014 these add files for OTHER tools)",
|
|
474
|
+
choices: [
|
|
475
|
+
{ name: "Cursor (.cursor/rules/bujang-*.mdc)", value: "cursor", checked: isPreset("cursor") },
|
|
476
|
+
{ name: "Codex / Copilot (AGENTS.md)", value: "codex", checked: isPreset("codex") },
|
|
477
|
+
{ name: "Cline (.clinerules/bujang-*.md)", value: "cline", checked: isPreset("cline") },
|
|
478
|
+
{ name: "Aider (CONVENTIONS.md + .aider.conf.yml)", value: "aider", checked: isPreset("aider") },
|
|
479
|
+
{ name: "Gemini / Antigravity (GEMINI.md + .gemini/styleguide.md)", value: "gemini", checked: isPreset("gemini") }
|
|
480
|
+
],
|
|
481
|
+
required: false
|
|
482
|
+
});
|
|
483
|
+
const preset = await select({
|
|
484
|
+
message: "Per-agent Claude model? (only affects .claude/agents/ \u2014 other tools manage models themselves)",
|
|
485
|
+
choices: [
|
|
486
|
+
{ name: "balanced \u2014 opus / sonnet / haiku mix (recommended, ~60% cost cut)", value: "balanced" },
|
|
487
|
+
{ name: "keep \u2014 leave each agent's default model untouched", value: "keep" },
|
|
488
|
+
{ name: "cost \u2014 all haiku (cheapest, fastest)", value: "cost" },
|
|
489
|
+
{ name: "quality \u2014 all opus (most expensive, highest quality)", value: "quality" },
|
|
490
|
+
{ name: "custom \u2014 pick model per agent (18 prompts)", value: "custom" }
|
|
491
|
+
],
|
|
492
|
+
default: "balanced"
|
|
493
|
+
});
|
|
494
|
+
let modelMap = {};
|
|
495
|
+
if (preset === "custom") {
|
|
496
|
+
modelMap = await promptCustomModelMap();
|
|
497
|
+
} else {
|
|
498
|
+
modelMap = resolvePreset(preset);
|
|
499
|
+
}
|
|
434
500
|
let installTemplate = opts.installTemplate;
|
|
435
501
|
if (scan.framework.startsWith("Next.js") && opts.installTemplate) {
|
|
436
502
|
installTemplate = await confirm({
|
|
@@ -438,7 +504,26 @@ async function promptInteractive(opts, scan) {
|
|
|
438
504
|
default: true
|
|
439
505
|
});
|
|
440
506
|
}
|
|
441
|
-
return { ...opts, lang, chatBackend, installTemplate };
|
|
507
|
+
return { ...opts, lang, chatBackend, installTemplate, adapters, modelMap };
|
|
508
|
+
}
|
|
509
|
+
async function promptCustomModelMap() {
|
|
510
|
+
const out = {};
|
|
511
|
+
const slugs = Object.keys(BALANCED_MAPPING);
|
|
512
|
+
console.log();
|
|
513
|
+
console.log(c.dim(` Custom mapping \u2014 pick a model for each of ${slugs.length} agents.`));
|
|
514
|
+
for (const slug of slugs) {
|
|
515
|
+
const tier = await select({
|
|
516
|
+
message: `${slug.padEnd(20)}`,
|
|
517
|
+
choices: [
|
|
518
|
+
{ name: "opus (heaviest, smartest)", value: "opus" },
|
|
519
|
+
{ name: "sonnet (balanced)", value: "sonnet" },
|
|
520
|
+
{ name: "haiku (lightest, fastest, cheap)", value: "haiku" }
|
|
521
|
+
],
|
|
522
|
+
default: BALANCED_MAPPING[slug] ?? "sonnet"
|
|
523
|
+
});
|
|
524
|
+
out[slug] = tier;
|
|
525
|
+
}
|
|
526
|
+
return out;
|
|
442
527
|
}
|
|
443
528
|
function parseArgs(args) {
|
|
444
529
|
const lang = getFlag(args, "--lang") ?? "ko";
|
|
@@ -450,6 +535,30 @@ function parseArgs(args) {
|
|
|
450
535
|
throw new Error(`--chat must be "sqlite" or "supabase", got "${chatBackend}"`);
|
|
451
536
|
}
|
|
452
537
|
const targetRaw = getFlag(args, "--target") ?? ".";
|
|
538
|
+
const toolsRaw = getFlag(args, "--tools");
|
|
539
|
+
let adapters = [];
|
|
540
|
+
if (toolsRaw) {
|
|
541
|
+
const parts = toolsRaw === "all" ? ALL_ADAPTERS.slice() : toolsRaw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
542
|
+
for (const t of parts) {
|
|
543
|
+
if (t === "claude" || t === "claude-code") continue;
|
|
544
|
+
if (!ALL_ADAPTERS.includes(t)) {
|
|
545
|
+
throw new Error(
|
|
546
|
+
`Unknown tool "${t}" in --tools. Allowed: claude, ${ALL_ADAPTERS.join(", ")}, all`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
if (!adapters.includes(t)) adapters.push(t);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const modelsRaw = getFlag(args, "--models");
|
|
553
|
+
let modelMap = {};
|
|
554
|
+
if (modelsRaw) {
|
|
555
|
+
if (!["balanced", "cost", "quality", "keep"].includes(modelsRaw)) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
`--models must be one of: balanced, cost, quality, keep (got "${modelsRaw}")`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
modelMap = resolvePreset(modelsRaw);
|
|
561
|
+
}
|
|
453
562
|
return {
|
|
454
563
|
lang,
|
|
455
564
|
target: path2.resolve(targetRaw),
|
|
@@ -460,9 +569,19 @@ function parseArgs(args) {
|
|
|
460
569
|
installTemplate: !args.includes("--no-template"),
|
|
461
570
|
editClaudeMd: !args.includes("--no-claude-md"),
|
|
462
571
|
seedLearningLog: !args.includes("--no-learning-log"),
|
|
463
|
-
yes: args.includes("--yes") || args.includes("-y")
|
|
572
|
+
yes: args.includes("--yes") || args.includes("-y"),
|
|
573
|
+
adapters,
|
|
574
|
+
modelMap
|
|
464
575
|
};
|
|
465
576
|
}
|
|
577
|
+
function resolvePreset(preset) {
|
|
578
|
+
if (preset === "keep") return {};
|
|
579
|
+
if (preset === "balanced") return { ...BALANCED_MAPPING };
|
|
580
|
+
const tier = preset === "cost" ? "haiku" : "opus";
|
|
581
|
+
const out = {};
|
|
582
|
+
for (const k of Object.keys(BALANCED_MAPPING)) out[k] = tier;
|
|
583
|
+
return out;
|
|
584
|
+
}
|
|
466
585
|
function getFlag(args, name) {
|
|
467
586
|
for (const a of args) {
|
|
468
587
|
if (a.startsWith(`${name}=`)) return a.slice(name.length + 1);
|
|
@@ -571,6 +690,26 @@ function printBackendInstructions(backend, commitChat) {
|
|
|
571
690
|
}
|
|
572
691
|
console.log();
|
|
573
692
|
}
|
|
693
|
+
function overrideModelFrontmatter(content, model) {
|
|
694
|
+
if (!content.startsWith("---\n")) return content;
|
|
695
|
+
const end = content.indexOf("\n---\n", 4);
|
|
696
|
+
if (end < 0) return content;
|
|
697
|
+
const fmRaw = content.slice(0, end);
|
|
698
|
+
const rest = content.slice(end);
|
|
699
|
+
const newFm = /^model:\s*\S+/m.test(fmRaw) ? fmRaw.replace(/^model:\s*\S+/m, `model: ${model}`) : fmRaw + `
|
|
700
|
+
model: ${model}`;
|
|
701
|
+
return newFm + rest;
|
|
702
|
+
}
|
|
703
|
+
function describeModelMap(map) {
|
|
704
|
+
if (Object.keys(map).length === 0) return "keep (use each agent's default)";
|
|
705
|
+
const counts = { opus: 0, sonnet: 0, haiku: 0 };
|
|
706
|
+
for (const v of Object.values(map)) counts[v]++;
|
|
707
|
+
const parts = [];
|
|
708
|
+
for (const tier of ["opus", "sonnet", "haiku"]) {
|
|
709
|
+
if (counts[tier] > 0) parts.push(`${counts[tier]} ${tier}`);
|
|
710
|
+
}
|
|
711
|
+
return parts.join(" \xB7 ");
|
|
712
|
+
}
|
|
574
713
|
function stackReviewRules(framework) {
|
|
575
714
|
if (framework.startsWith("Next.js")) {
|
|
576
715
|
return `Next.js App Router rules:
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import * as fs from "fs/promises";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
var __dirname = path.dirname(__filename);
|
|
9
|
+
async function readVersion() {
|
|
10
|
+
try {
|
|
11
|
+
const pkgRaw = await fs.readFile(path.resolve(__dirname, "..", "package.json"), "utf8");
|
|
12
|
+
return JSON.parse(pkgRaw).version ?? "unknown";
|
|
13
|
+
} catch {
|
|
14
|
+
return "unknown";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
var c = {
|
|
5
18
|
bold: (s) => `\x1B[1m${s}\x1B[22m`,
|
|
6
19
|
dim: (s) => `\x1B[2m${s}\x1B[22m`,
|
|
@@ -25,6 +38,10 @@ ${c.bold("Options for init:")}
|
|
|
25
38
|
--lang=<ko|en> Agent language (default: ko \u2014 full \uBD80\uC7A5 persona)
|
|
26
39
|
--chat=<sqlite|supabase> Chat-room backend (default: sqlite \u2014 local file, no setup)
|
|
27
40
|
--commit-chat Don't gitignore .harness/ (for solo cross-machine sync via git)
|
|
41
|
+
--tools=<list> Extra tool adapters: cursor,cline,aider,codex,gemini,all
|
|
42
|
+
(Claude Code is always installed at .claude/agents/)
|
|
43
|
+
--models=<preset> Per-agent Claude model preset: balanced (recommended),
|
|
44
|
+
keep (default), cost (all haiku), quality (all opus)
|
|
28
45
|
--target=<path> Project root (default: cwd)
|
|
29
46
|
--framework=<name> Override detected framework
|
|
30
47
|
--db=<name> Override detected project DB (separate from --chat)
|
|
@@ -90,7 +107,7 @@ async function main() {
|
|
|
90
107
|
const command = args[0];
|
|
91
108
|
switch (command) {
|
|
92
109
|
case "init":
|
|
93
|
-
await (await import("./init-
|
|
110
|
+
await (await import("./init-4G7R63DX.js")).runInit(args.slice(1));
|
|
94
111
|
break;
|
|
95
112
|
case "status":
|
|
96
113
|
await (await import("./status-UE2TQQPU.js")).runStatus(args.slice(1));
|
|
@@ -102,14 +119,14 @@ async function main() {
|
|
|
102
119
|
await (await import("./adapt-VPWOYF6W.js")).runAdapt(args.slice(1));
|
|
103
120
|
break;
|
|
104
121
|
case "update":
|
|
105
|
-
await (await import("./update-
|
|
122
|
+
await (await import("./update-KHDRQZQE.js")).runUpdate(args.slice(1));
|
|
106
123
|
break;
|
|
107
124
|
case "migrate":
|
|
108
125
|
await (await import("./migrate-PISZFX6C.js")).runMigrate(args.slice(1));
|
|
109
126
|
break;
|
|
110
127
|
case "--version":
|
|
111
128
|
case "-v":
|
|
112
|
-
console.log(
|
|
129
|
+
console.log(await readVersion());
|
|
113
130
|
break;
|
|
114
131
|
case "--help":
|
|
115
132
|
case "-h":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-bujang",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Install the Harness-Bujang multi-agent harness into any project — Director, 7 specialist teams, real-time chat-room UI. Korean and English personas. Works with Claude Code, Cursor, Cline, Aider, or any tool that reads .claude/agents/.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -59,6 +59,14 @@ Co-founder is not the Director's boss — they're **co-decision-makers**. Don't
|
|
|
59
59
|
|
|
60
60
|
## Chat-room INSERT pattern
|
|
61
61
|
|
|
62
|
+
### 🔒 1:1 mapping rule (same as Director)
|
|
63
|
+
|
|
64
|
+
**One `Agent` tool call = one `harness_messages` INSERT.** Parallel or sequential. Applies whenever the Co-founder pulls in-house teams (`research-team` / `analysis-team` / `consultant` / `architect-team`).
|
|
65
|
+
|
|
66
|
+
- Parallel calls → INSERT N rows (one per team, `from='공동대표' to='<team>' type='command'`)
|
|
67
|
+
- On results → INSERT (`from='<team>' to='공동대표' type='report'`)
|
|
68
|
+
- No Agent call without an INSERT.
|
|
69
|
+
|
|
62
70
|
Co-founder's voice goes to the **'공동대표' (cofounder) room**.
|
|
63
71
|
|
|
64
72
|
```bash
|
|
@@ -40,10 +40,20 @@ When the principal says "Director, …", Main Claude reads this file as a system
|
|
|
40
40
|
|
|
41
41
|
INSERT into `{{HARNESS_TABLE}}` at every step. Main Claude proxies each role.
|
|
42
42
|
|
|
43
|
+
### 🔒 1:1 mapping rule — never violate
|
|
44
|
+
|
|
45
|
+
**One `Agent` tool call = one `harness_messages` INSERT.** Parallel or sequential, no exception.
|
|
46
|
+
|
|
47
|
+
- Spinning up N teams in parallel → INSERT N rows **right before or simultaneously with** dispatch (one per team)
|
|
48
|
+
- One pre-confirm ("planning to invoke N teams") → principal OK → INSERT N rows → Agent N calls → on results, INSERT N rows (`from='<team>' type='report'`)
|
|
49
|
+
- No Agent call without an INSERT. If missed, file a retroactive INSERT + entry in the learning log immediately.
|
|
50
|
+
- **Fixed order**: pre-confirm → INSERT → Agent call → result INSERT (mandatory except for 1–2 line hotfixes / plain Q&A)
|
|
51
|
+
- Even a trivial 1-line fix gets one director-named INSERT (audit trail)
|
|
52
|
+
|
|
43
53
|
### When to INSERT (do not skip)
|
|
44
54
|
|
|
45
55
|
1. **On receiving a command** — `type='command'`, 1–2 line summary
|
|
46
|
-
2. **
|
|
56
|
+
2. **Right before / during dispatch** — `type='command'`, target / scope (one row per team if parallel)
|
|
47
57
|
3. **On completion** — `type='report'`, summarized result
|
|
48
58
|
4. **On failure / blocker** — `severity='warning'+` immediately
|
|
49
59
|
5. **On external-tool calls** — separate INSERT with `from='외부팀원'` (external-team room)
|
|
@@ -59,6 +59,14 @@ model: opus
|
|
|
59
59
|
|
|
60
60
|
## 톡방 INSERT 패턴
|
|
61
61
|
|
|
62
|
+
### 🔒 1:1 매핑 룰 (부장과 동일)
|
|
63
|
+
|
|
64
|
+
**Agent 툴 호출 1번 = 톡방 INSERT 1행.** 병렬·순차 무관. 공동대표가 사내 팀 (`research-team` / `analysis-team` / `consultant` / `architect-team`) 호출할 때도 동일 적용.
|
|
65
|
+
|
|
66
|
+
- 병렬 호출 = INSERT N행 (각 팀별 1건, `from='공동대표' to='<팀>' type='command'`)
|
|
67
|
+
- 결과 받으면 INSERT (`from='<팀>' to='공동대표' type='report'`)
|
|
68
|
+
- INSERT 없이 Agent 호출 금지
|
|
69
|
+
|
|
62
70
|
공동대표 발언은 **공동대표 톡방** (`'공동대표'`) 에 기록.
|
|
63
71
|
|
|
64
72
|
```bash
|
|
@@ -40,10 +40,20 @@ Main Claude (= 부장)
|
|
|
40
40
|
|
|
41
41
|
모든 작업 단계에서 `{{HARNESS_TABLE}}` INSERT 필수. Main Claude가 각 역할 명의로 대행.
|
|
42
42
|
|
|
43
|
+
### 🔒 1:1 매핑 룰 — 절대 어기지 말 것
|
|
44
|
+
|
|
45
|
+
**Agent 툴 호출 1번 = 톡방 INSERT 1행.** 병렬이든 순차든 무관.
|
|
46
|
+
|
|
47
|
+
- 병렬로 N팀 띄울 거면 → 디스패치 **직전 또는 동시** 에 INSERT N행 (각 팀별 1건)
|
|
48
|
+
- "다음 N팀 부르려고 합니다" 사전 동의 1번 → 대표님 OK → 디스패치 직전 INSERT N행 → Agent N건 호출 → 결과 받으면 INSERT N행 (`from='<팀>' type='report'`)
|
|
49
|
+
- INSERT 없이 Agent 호출 금지. 누락 발견 시 즉시 사후 INSERT + 학습 로그 기재
|
|
50
|
+
- **사전 동의 → INSERT → Agent 호출** 순서 고정 (핫픽스 1~2줄·단순 답변 외엔 무조건)
|
|
51
|
+
- 트리비얼 한 줄 픽스도 부장 명의 INSERT 1건은 박는다 ("[NOTE] X.tsx 오타 1줄 직접 수정")
|
|
52
|
+
|
|
43
53
|
### 언제 INSERT (누락 금지)
|
|
44
54
|
|
|
45
55
|
1. **지시 수신 직후** — `type='command'`, 요약 1~2줄
|
|
46
|
-
2.
|
|
56
|
+
2. **디스패치 직전·동시** — `type='command'`, 위임 대상·범위 (병렬이면 각 팀별 1건)
|
|
47
57
|
3. **완료 보고 시** — `type='report'`, 결과 요약
|
|
48
58
|
4. **실패·블로커** — `severity='warning'` 이상 즉시
|
|
49
59
|
5. **외부 도구 호출 시** — from='외부팀원' 으로 별도 INSERT (외부팀원 톡방)
|
|
@@ -40,7 +40,7 @@ Main Claude (= Director persona)
|
|
|
40
40
|
INSERT into `{{HARNESS_TABLE}}` at every major step. Main Claude proxies each role:
|
|
41
41
|
|
|
42
42
|
1. On receiving a command — `from='principal' to='director' type='command'`
|
|
43
|
-
2.
|
|
43
|
+
2. Right before / during dispatch — `from='director' to='<team>' type='command'` (one row per team if parallel)
|
|
44
44
|
3. On team completion — `from='<team>' to='director' type='report'`
|
|
45
45
|
4. Final principal report — `from='director' to='principal' type='report'` (principal-report room — never skip)
|
|
46
46
|
5. Failure / blocker — `severity='warning'+` immediately
|
|
@@ -49,21 +49,32 @@ Schema: `id · timestamp · from · to · type · message · severity · data ·
|
|
|
49
49
|
type CHECK: `command|feedback|info|report` · severity: `info|warning|error`
|
|
50
50
|
Format: markdown line breaks, bullet points (no prose blobs). First line: `[PASS] / [FAIL] / [POLICY] / [NOTE]` tag.
|
|
51
51
|
|
|
52
|
+
### 🔒 1:1 mapping rule — Agent call = INSERT (never violate)
|
|
53
|
+
|
|
54
|
+
**One `Agent` tool call = one `harness_messages` INSERT row.** Parallel or sequential, no exception.
|
|
55
|
+
|
|
56
|
+
- Spinning up N teams in parallel → INSERT N rows **right before or simultaneously with** dispatch
|
|
57
|
+
- No Agent call without an INSERT. If missed, file a retroactive INSERT + entry in the learning log (`{{LEARNING_LOG_PATH}}`) immediately.
|
|
58
|
+
- **Fixed order**: pre-confirm → INSERT → Agent call → result INSERT
|
|
59
|
+
- Even a trivial 1-line direct fix gets one director-named INSERT (audit trail)
|
|
60
|
+
|
|
61
|
+
This rule applies to both the Director and Co-founder personas.
|
|
62
|
+
|
|
52
63
|
### 🚦 Pre-dispatch confirmation (required)
|
|
53
64
|
|
|
54
|
-
**Always propose the dispatch plan to the principal before invoking teams.**
|
|
65
|
+
**Always propose the dispatch plan to the principal before invoking teams.** No invoking N teams on a whim.
|
|
55
66
|
|
|
56
67
|
```
|
|
57
|
-
"Plan to invoke:
|
|
68
|
+
"Plan to invoke (parallel):
|
|
58
69
|
- architect-team — structure
|
|
59
70
|
- security-team — security impact
|
|
60
|
-
Estimated ~5 min
|
|
71
|
+
Estimated ~5 min — will write 2 chat INSERTs and dispatch.
|
|
61
72
|
Proceed?"
|
|
62
73
|
```
|
|
63
74
|
|
|
64
|
-
Principal OK →
|
|
75
|
+
Principal OK → INSERT N rows → invoke N Agent calls. Add / drop / tweak → revise and re-confirm.
|
|
65
76
|
|
|
66
|
-
**Exceptions** (skip pre-confirm OK): 1–2 line hotfixes / plain Q&A / pre-approved by principal.
|
|
77
|
+
**Exceptions** (skip pre-confirm OK): 1–2 line hotfixes / plain Q&A / pre-approved by principal. (A retroactive single chat INSERT is still required.)
|
|
67
78
|
|
|
68
79
|
### 🌐 In-house teams vs external tools
|
|
69
80
|
|
|
@@ -40,7 +40,7 @@ Main Claude (= 부장 페르소나)
|
|
|
40
40
|
모든 주요 단계에서 `{{HARNESS_TABLE}}` INSERT. Main Claude가 각 역할 명의로 대행:
|
|
41
41
|
|
|
42
42
|
1. 지시 수신 직후 — `from='대표님' to='부장' type='command'`
|
|
43
|
-
2.
|
|
43
|
+
2. 디스패치 직전·동시 — `from='부장' to='<팀>' type='command'` (병렬이면 각 팀별 1건)
|
|
44
44
|
3. 팀 완료 보고 — `from='<팀>' to='부장' type='report'`
|
|
45
45
|
4. 대표님 최종 보고 — `from='부장' to='대표님' type='report'` (대표 보고 톡방, 누락 금지)
|
|
46
46
|
5. 실패·블로커 — `severity='warning'` 이상 즉시
|
|
@@ -49,21 +49,32 @@ Main Claude (= 부장 페르소나)
|
|
|
49
49
|
type CHECK: `command|feedback|info|report` · severity: `info|warning|error`
|
|
50
50
|
메시지 포맷: 마크다운 줄바꿈·개조식 (줄글 금지). 첫 줄 `[PASS] / [FAIL] / [POLICY] / [NOTE]` 태그.
|
|
51
51
|
|
|
52
|
+
### 🔒 1:1 매핑 룰 — Agent 호출 = INSERT (절대 어기지 말 것)
|
|
53
|
+
|
|
54
|
+
**`Agent` 툴 한 번 호출 = `harness_messages` INSERT 한 행.** 병렬·순차 무관.
|
|
55
|
+
|
|
56
|
+
- 병렬로 N팀 띄울 거면 → 디스패치 **직전 또는 동시** 에 INSERT N행
|
|
57
|
+
- INSERT 없이 Agent 호출 금지. 누락 발견 시 즉시 사후 INSERT + 학습 로그 (`{{LEARNING_LOG_PATH}}`) 에 기재
|
|
58
|
+
- **순서 고정**: 사전 동의 → INSERT → Agent 호출 → 결과 INSERT
|
|
59
|
+
- 트리비얼 1줄 직접 수정도 부장 명의 INSERT 1행은 박는다 (감사 추적용)
|
|
60
|
+
|
|
61
|
+
이 룰은 부장·공동대표 페르소나 양쪽에 모두 적용됨.
|
|
62
|
+
|
|
52
63
|
### 🚦 사전 동의 프로토콜 (디스패치 전 필수)
|
|
53
64
|
|
|
54
|
-
**팀을 부르기 전에 항상 대표님께 계획 보고 후 승인.** 무작정
|
|
65
|
+
**팀을 부르기 전에 항상 대표님께 계획 보고 후 승인.** 무작정 N팀 호출 금지.
|
|
55
66
|
|
|
56
67
|
```
|
|
57
|
-
"다음 팀 부르려고
|
|
68
|
+
"다음 팀 부르려고 합니다 (병렬):
|
|
58
69
|
- architect-team — 구조 설계
|
|
59
70
|
- security-team — 보안 영향
|
|
60
|
-
예상 ~5분,
|
|
71
|
+
예상 ~5분, 톡방에 INSERT 2건 박고 디스패치합니다.
|
|
61
72
|
진행할까요?"
|
|
62
73
|
```
|
|
63
74
|
|
|
64
|
-
대표님 OK →
|
|
75
|
+
대표님 OK → INSERT N건 박기 → Agent N건 호출. 추가/제외/수정 요구 → 반영 후 재확인.
|
|
65
76
|
|
|
66
|
-
**예외 (사전 동의 생략 OK)**: 핫픽스 1~2줄 / 단순 질문 답변 / 대표님 명시적 사전 승인.
|
|
77
|
+
**예외 (사전 동의 생략 OK)**: 핫픽스 1~2줄 / 단순 질문 답변 / 대표님 명시적 사전 승인. (단, 사후 톡방 INSERT 1행은 항상 필수.)
|
|
67
78
|
|
|
68
79
|
### 🌐 사내 팀 vs 외부 도구
|
|
69
80
|
|