tycono 0.1.94-beta.0 → 0.1.94-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.94-beta.0",
3
+ "version": "0.1.94-beta.2",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -172,7 +172,12 @@ export async function runAgentLoop(config: AgentConfig): Promise<AgentResult> {
172
172
  const hasBash = !readOnly && !!config.codeRoot;
173
173
  const node = orgTree.nodes.get(roleId);
174
174
  const heartbeatEnabled = node?.heartbeat?.enabled === true && subordinates.length > 0;
175
- const tools = getToolsForRole(subordinates.length > 0, readOnly, hasBash, heartbeatEnabled);
175
+ // Peers = other roles with the same reportsTo (same parent in org tree)
176
+ const parentId = node?.reportsTo;
177
+ const hasPeers = parentId
178
+ ? (getSubordinates(orgTree, parentId).filter(id => id !== roleId).length > 0)
179
+ : false;
180
+ const tools = getToolsForRole(subordinates.length > 0, readOnly, hasBash, heartbeatEnabled, hasPeers);
176
181
 
177
182
  // 3. Set up tool executor
178
183
  const toolExecOptions: ToolExecutorOptions = {
@@ -550,163 +555,63 @@ export async function runAgentLoop(config: AgentConfig): Promise<AgentResult> {
550
555
  }
551
556
  }
552
557
 
553
- // Phase B: Engineer/CTO Verificationtype checking + visual verification
554
- const verifiableRoles = ['engineer', 'cto'];
555
- if (verifiableRoles.includes(roleId)) {
556
- const hasFileChanges = allToolCalls.some((tc) =>
557
- ['write', 'edit', 'bash'].includes(tc.name.toLowerCase()),
558
- );
558
+ // Detect file changes onceused by Phase B and Post-K
559
+ const hasFileChanges = allToolCalls.some((tc) =>
560
+ ['write', 'edit', 'bash'].includes(tc.name.toLowerCase()),
561
+ );
559
562
 
560
- if (hasFileChanges) {
561
- const verifyPrompt = [
562
- '[AUTO-VERIFICATION] 작업이 완료되었습니다. 아래 검증을 수행하세요:',
563
- '1. `cd src/api && npx tsc --noEmit` — 타입 에러 확인',
564
- '2. `cd src/web && npx tsc --noEmit` — 프론트엔드 타입 에러 확인',
565
- '3. UI/CSS 변경이 있었다면 Playwright MCP로 스크린샷을 촬영하여 시각 검증',
566
- '검증 결과를 간단히 보고하세요.',
567
- ].join('\n');
568
-
569
- messages.push({ role: 'user', content: verifyPrompt });
570
-
571
- if (turns < maxTurns) {
572
- turns++;
573
- const verifyResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
574
- totalInput += verifyResponse.usage.inputTokens;
575
- totalOutput += verifyResponse.usage.outputTokens;
576
- config.tokenLedger?.record({
577
- ts: new Date().toISOString(),
578
- sessionId: config.sessionId,
579
- roleId,
580
- model: config.model ?? 'unknown',
581
- inputTokens: verifyResponse.usage.inputTokens,
582
- outputTokens: verifyResponse.usage.outputTokens,
583
- });
563
+ // Phase B: Member Self-Verification — type checking + visual verification
564
+ // Any non-C-Level role that made file changes gets verification (no hardcoded role IDs)
565
+ if (!isCLevel && hasFileChanges) {
566
+ const verifyPrompt = [
567
+ '[AUTO-VERIFICATION] 작업이 완료되었습니다. 아래 검증을 수행하세요:',
568
+ '1. `cd src/api && npx tsc --noEmit` 타입 에러 확인',
569
+ '2. `cd src/web && npx tsc --noEmit` — 프론트엔드 타입 에러 확인',
570
+ '3. UI/CSS 변경이 있었다면 Playwright MCP로 스크린샷을 촬영하여 시각 검증',
571
+ '검증 결과를 간단히 보고하세요.',
572
+ ].join('\n');
584
573
 
585
- messages.push({ role: 'assistant', content: verifyResponse.content });
586
- for (const block of verifyResponse.content) {
587
- if (block.type === 'text' && block.text) {
588
- outputParts.push(block.text);
589
- onText?.(block.text);
590
- }
591
- }
574
+ messages.push({ role: 'user', content: verifyPrompt });
592
575
 
593
- // Execute verification tool calls if needed
594
- if (verifyResponse.stopReason === 'tool_use') {
595
- const verifyToolCalls = verifyResponse.content.filter(
596
- (b): b is MessageContent & { type: 'tool_use' } => b.type === 'tool_use',
597
- );
598
- const verifyResults: ToolResult[] = [];
599
- for (const tc of verifyToolCalls) {
600
- allToolCalls.push({ name: tc.name, input: tc.input });
601
- const result = await executeTool(
602
- { id: tc.id, name: tc.name, input: tc.input },
603
- toolExecOptions,
604
- );
605
- verifyResults.push(result);
606
- }
607
- messages.push({
608
- role: 'user',
609
- content: verifyResults.map((r) => ({
610
- type: 'tool_result' as const,
611
- tool_use_id: r.tool_use_id,
612
- content: r.content,
613
- is_error: r.is_error,
614
- })) as unknown as MessageContent[],
615
- });
576
+ if (turns < maxTurns) {
577
+ turns++;
578
+ const verifyResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
579
+ totalInput += verifyResponse.usage.inputTokens;
580
+ totalOutput += verifyResponse.usage.outputTokens;
581
+ config.tokenLedger?.record({
582
+ ts: new Date().toISOString(),
583
+ sessionId: config.sessionId,
584
+ roleId,
585
+ model: config.model ?? 'unknown',
586
+ inputTokens: verifyResponse.usage.inputTokens,
587
+ outputTokens: verifyResponse.usage.outputTokens,
588
+ });
616
589
 
617
- if (turns < maxTurns) {
618
- turns++;
619
- const summaryResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
620
- totalInput += summaryResponse.usage.inputTokens;
621
- totalOutput += summaryResponse.usage.outputTokens;
622
- config.tokenLedger?.record({
623
- ts: new Date().toISOString(),
624
- sessionId: config.sessionId,
625
- roleId,
626
- model: config.model ?? 'unknown',
627
- inputTokens: summaryResponse.usage.inputTokens,
628
- outputTokens: summaryResponse.usage.outputTokens,
629
- });
630
- for (const block of summaryResponse.content) {
631
- if (block.type === 'text' && block.text) {
632
- outputParts.push(block.text);
633
- onText?.(block.text);
634
- }
635
- }
636
- }
590
+ messages.push({ role: 'assistant', content: verifyResponse.content });
591
+ for (const block of verifyResponse.content) {
592
+ if (block.type === 'text' && block.text) {
593
+ outputParts.push(block.text);
594
+ onText?.(block.text);
637
595
  }
638
-
639
- onTurnComplete?.(turns);
640
596
  }
641
597
 
642
- // ── Post-Knowledging: ④⑤ 수행 프롬프트 (KP-008) ──
643
- // Engineer/CTO가 구현 완료 후 The Loop 마무리 (Knowledge 업데이트 + Task 상태 갱신) 수행하도록 유도
644
- const postKPrompt = [
645
- '[POST-KNOWLEDGING] 구현이 완료되었습니다. The Loop 마무리를 수행하세요:',
646
- '',
647
- '## ④ Knowledge 업데이트 (The Loop Step 4)',
648
- '다음 중 해당하는 항목을 수행하세요:',
649
- '- 본인 journal 업데이트 (`roles/' + roleId + '/journal/YYYY-MM-DD.md` — 오늘 날짜 파일)',
650
- '- 구현 중 새로 발견한 패턴/아키텍처 결정이 있다면 관련 문서 업데이트',
651
- ' (예: architecture/web-app-ia.md, architecture/session-worktree-isolation.md 등)',
652
- '- 중요한 기술 결정은 operations/decisions/ 또는 architecture/ 반영',
653
- '',
654
- '## ⑤ Task 상태 갱신 (The Loop Step 5)',
655
- '- `projects/tycono-platform/tasks.md` (또는 관련 tasks 파일)에서 완료한 태스크 상태를 DONE으로 변경',
656
- '- 다음 작업이 있다면 식별하여 메모',
657
- '',
658
- '⛔ **필수**: ④와 ⑤를 모두 수행해야 The Loop이 완료됩니다.',
659
- '이제 ④⑤를 수행하세요 (Read, Edit 도구 사용).',
660
- ].join('\n');
661
-
662
- messages.push({ role: 'user', content: postKPrompt });
663
-
664
- // Run Post-K loop (최대 2턴 — C-Level의 3턴보다 짧게)
665
- const maxPostKRounds = 2;
666
- for (let round = 0; round < maxPostKRounds && turns < maxTurns; round++) {
667
- if (abortSignal?.aborted) break;
668
- turns++;
669
-
670
- const postKResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
671
- totalInput += postKResponse.usage.inputTokens;
672
- totalOutput += postKResponse.usage.outputTokens;
673
- config.tokenLedger?.record({
674
- ts: new Date().toISOString(),
675
- sessionId: config.sessionId,
676
- roleId,
677
- model: config.model ?? 'unknown',
678
- inputTokens: postKResponse.usage.inputTokens,
679
- outputTokens: postKResponse.usage.outputTokens,
680
- });
681
-
682
- messages.push({ role: 'assistant', content: postKResponse.content });
683
- for (const block of postKResponse.content) {
684
- if (block.type === 'text' && block.text) {
685
- outputParts.push(block.text);
686
- onText?.(block.text);
687
- }
688
- }
689
-
690
- // If no tool calls, Post-K is done
691
- if (postKResponse.stopReason !== 'tool_use') break;
692
-
693
- // Execute Post-K tool calls
694
- const postKToolCalls = postKResponse.content.filter(
598
+ // Execute verification tool calls if needed
599
+ if (verifyResponse.stopReason === 'tool_use') {
600
+ const verifyToolCalls = verifyResponse.content.filter(
695
601
  (b): b is MessageContent & { type: 'tool_use' } => b.type === 'tool_use',
696
602
  );
697
- const postKResults: ToolResult[] = [];
698
- for (const tc of postKToolCalls) {
603
+ const verifyResults: ToolResult[] = [];
604
+ for (const tc of verifyToolCalls) {
699
605
  allToolCalls.push({ name: tc.name, input: tc.input });
700
606
  const result = await executeTool(
701
607
  { id: tc.id, name: tc.name, input: tc.input },
702
608
  toolExecOptions,
703
609
  );
704
- postKResults.push(result);
610
+ verifyResults.push(result);
705
611
  }
706
-
707
612
  messages.push({
708
613
  role: 'user',
709
- content: postKResults.map((r) => ({
614
+ content: verifyResults.map((r) => ({
710
615
  type: 'tool_result' as const,
711
616
  tool_use_id: r.tool_use_id,
712
617
  content: r.content,
@@ -714,8 +619,110 @@ export async function runAgentLoop(config: AgentConfig): Promise<AgentResult> {
714
619
  })) as unknown as MessageContent[],
715
620
  });
716
621
 
717
- onTurnComplete?.(turns);
622
+ if (turns < maxTurns) {
623
+ turns++;
624
+ const summaryResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
625
+ totalInput += summaryResponse.usage.inputTokens;
626
+ totalOutput += summaryResponse.usage.outputTokens;
627
+ config.tokenLedger?.record({
628
+ ts: new Date().toISOString(),
629
+ sessionId: config.sessionId,
630
+ roleId,
631
+ model: config.model ?? 'unknown',
632
+ inputTokens: summaryResponse.usage.inputTokens,
633
+ outputTokens: summaryResponse.usage.outputTokens,
634
+ });
635
+ for (const block of summaryResponse.content) {
636
+ if (block.type === 'text' && block.text) {
637
+ outputParts.push(block.text);
638
+ onText?.(block.text);
639
+ }
640
+ }
641
+ }
642
+ }
643
+
644
+ onTurnComplete?.(turns);
645
+ }
646
+ }
647
+
648
+ // ── Post-K: ④⑤ Knowledge/Task update (KP-008) ──
649
+ // ALL roles (C-Level and members) update journal/tasks after significant work.
650
+ // Runs for: members who made file changes, C-Level who dispatched work.
651
+ if (hasFileChanges || dispatches.length > 0) {
652
+ const postKPrompt = [
653
+ '[POST-KNOWLEDGING] 작업이 완료되었습니다. The Loop 마무리를 수행하세요:',
654
+ '',
655
+ '## ④ Knowledge 업데이트 (The Loop Step 4)',
656
+ '다음 중 해당하는 항목을 수행하세요:',
657
+ '- 본인 journal 업데이트 (`roles/' + roleId + '/journal/YYYY-MM-DD.md` — 오늘 날짜 파일)',
658
+ '- 구현 중 새로 발견한 패턴/아키텍처 결정이 있다면 관련 문서 업데이트',
659
+ ' (예: architecture/web-app-ia.md, architecture/session-worktree-isolation.md 등)',
660
+ '- 중요한 기술 결정은 operations/decisions/ 또는 architecture/ 반영',
661
+ '',
662
+ '## ⑤ Task 상태 갱신 (The Loop Step 5)',
663
+ '- `projects/tycono-platform/tasks.md` (또는 관련 tasks 파일)에서 완료한 태스크 상태를 DONE으로 변경',
664
+ '- 다음 작업이 있다면 식별하여 메모',
665
+ '',
666
+ '⛔ **필수**: ④와 ⑤를 모두 수행해야 The Loop이 완료됩니다.',
667
+ '이제 ④⑤를 수행하세요 (Read, Edit 도구 사용).',
668
+ ].join('\n');
669
+
670
+ messages.push({ role: 'user', content: postKPrompt });
671
+
672
+ // Run Post-K loop (최대 2턴)
673
+ const maxPostKRounds = 2;
674
+ for (let round = 0; round < maxPostKRounds && turns < maxTurns; round++) {
675
+ if (abortSignal?.aborted) break;
676
+ turns++;
677
+
678
+ const postKResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
679
+ totalInput += postKResponse.usage.inputTokens;
680
+ totalOutput += postKResponse.usage.outputTokens;
681
+ config.tokenLedger?.record({
682
+ ts: new Date().toISOString(),
683
+ sessionId: config.sessionId,
684
+ roleId,
685
+ model: config.model ?? 'unknown',
686
+ inputTokens: postKResponse.usage.inputTokens,
687
+ outputTokens: postKResponse.usage.outputTokens,
688
+ });
689
+
690
+ messages.push({ role: 'assistant', content: postKResponse.content });
691
+ for (const block of postKResponse.content) {
692
+ if (block.type === 'text' && block.text) {
693
+ outputParts.push(block.text);
694
+ onText?.(block.text);
695
+ }
696
+ }
697
+
698
+ // If no tool calls, Post-K is done
699
+ if (postKResponse.stopReason !== 'tool_use') break;
700
+
701
+ // Execute Post-K tool calls
702
+ const postKToolCalls = postKResponse.content.filter(
703
+ (b): b is MessageContent & { type: 'tool_use' } => b.type === 'tool_use',
704
+ );
705
+ const postKResults: ToolResult[] = [];
706
+ for (const tc of postKToolCalls) {
707
+ allToolCalls.push({ name: tc.name, input: tc.input });
708
+ const result = await executeTool(
709
+ { id: tc.id, name: tc.name, input: tc.input },
710
+ toolExecOptions,
711
+ );
712
+ postKResults.push(result);
718
713
  }
714
+
715
+ messages.push({
716
+ role: 'user',
717
+ content: postKResults.map((r) => ({
718
+ type: 'tool_result' as const,
719
+ tool_use_id: r.tool_use_id,
720
+ content: r.content,
721
+ is_error: r.is_error,
722
+ })) as unknown as MessageContent[],
723
+ });
724
+
725
+ onTurnComplete?.(turns);
719
726
  }
720
727
  }
721
728
  }
@@ -138,6 +138,11 @@ Use the code repository path for all source code work (reading, writing, buildin
138
138
  sections.push(buildSupervisionSection(node));
139
139
  }
140
140
 
141
+ // Knowledge consistency management (C-Level responsibility)
142
+ if (node.level === 'c-level') {
143
+ sections.push(buildKnowledgeManagementSection(roleId));
144
+ }
145
+
141
146
  // Dispatch 도구 안내 (하위 Role이 있는 경우)
142
147
  if (subordinates.length > 0) {
143
148
  sections.push(buildDispatchSection(orgTree, roleId, subordinates, options?.teamStatus));
@@ -526,36 +531,41 @@ ${subInfo}
526
531
 
527
532
  ## How to Dispatch
528
533
 
529
- **Dispatch is async: start a job → poll for result → review → next task.**
534
+ **Dispatch is async: start a job → supervise with watch → review → next task.**
530
535
 
531
536
  \`\`\`bash
532
- # Step 1: Dispatch (returns immediately with job ID)
537
+ # Step 1: Dispatch (returns immediately with session ID)
533
538
  python3 "$DISPATCH_CMD" ${exampleSubId} "Task description here"
539
+ # → Session ID: ses-xxx
534
540
 
535
- # Step 2: Poll for result (repeat every 10-30s until DONE)
536
- python3 "$DISPATCH_CMD" --check <jobId>
541
+ # Step 2: Supervise with watch (preferred) or poll with --check
542
+ # Option A (preferred): Supervision watch — blocks server-side, returns digest
543
+ python3 "$SUPERVISION_CMD" watch ses-xxx --duration 120
544
+ # Option B (fallback): Poll for result
545
+ python3 "$DISPATCH_CMD" --check ses-xxx
537
546
  \`\`\`
538
547
 
539
548
  ⛔ **NEVER use the Agent tool or Task tool to spawn sub-agents.** Those bypass the job tracking system. Use ONLY the dispatch command.
540
549
 
541
- ### The Pattern: Dispatch → PollReviewNext
550
+ ### The Pattern: Dispatch → WatchAnalyzeAct
542
551
 
543
552
  \`\`\`bash
544
- # 1. Dispatch first task (returns job ID immediately)
553
+ # 1. Dispatch tasks (save all session IDs)
545
554
  python3 "$DISPATCH_CMD" ${exampleSubId} "Task A"
546
- # Output includes: Job ID: job-xxx
555
+ # Session ID: ses-aaa
556
+ ${subordinates.length > 1 ? `python3 "$DISPATCH_CMD" ${subordinates[1]} "Task B"\n# → Session ID: ses-bbb` : ''}
547
557
 
548
- # 2. Poll for result (repeat until status is DONE)
549
- python3 "$DISPATCH_CMD" --check job-xxx
550
- # If RUNNING wait 10-30s --check again
551
- # If DONE → read the result → proceed to next task
558
+ # 2. Enter supervision loop watch ALL dispatched sessions
559
+ python3 "$SUPERVISION_CMD" watch ses-aaa${subordinates.length > 1 ? ',ses-bbb' : ''} --duration 120
560
+ # Returns digest with significance score + activity summary
552
561
 
553
- # 3. Dispatch next task
554
- python3 "$DISPATCH_CMD" ${subordinates.length > 1 ? subordinates[1] : exampleSubId} "Task B"
555
- python3 "$DISPATCH_CMD" --check job-yyy
556
- # ... repeat
562
+ # 3. Analyze digest → decide:
563
+ # - On track watch again
564
+ # - Off track → amend: python3 "$SUPERVISION_CMD" amend ses-aaa "Fix: use adapter pattern"
565
+ # - Failed → abort: python3 "$SUPERVISION_CMD" abort ses-aaa --reason "wrong approach"
566
+ # - Need input → consult: python3 "$CONSULT_CMD" cbo "Is this direction right?"
557
567
 
558
- # 4. Continue until ALL tasks are done
568
+ # 4. Repeat watch until ALL sessions complete
559
569
  \`\`\`
560
570
 
561
571
  ### --check Status Values
@@ -605,44 +615,52 @@ When you receive a directive:
605
615
 
606
616
  The loop:
607
617
  \`\`\`
608
- PLAN TASKS → DISPATCH → POLL (--check)REVIEW RESULT → DECIDE
609
- ├── PASSNext DIFFERENT Task
610
- └── FAILRe-dispatch with SPECIFIC fix
611
- └── ALL DONE Update knowledge → Report
618
+ PLAN TASKS → DISPATCH → SUPERVISION WATCHANALYZE DIGEST → DECIDE
619
+ ├── On track Watch again
620
+ ├── Off track Amend session
621
+ ├── FailedAbort + re-dispatch
622
+ └── ALL DONE → Update knowledge → Report
612
623
  \`\`\`
613
624
 
614
625
  ### ⛔ CRITICAL: No Duplicate Dispatch
615
626
 
616
627
  **NEVER dispatch the same or similar task to the same role twice.**
617
- - If --check shows RUNNING, keep polling — do NOT re-dispatch
628
+ - If watch shows sessions still running, keep watching — do NOT re-dispatch
618
629
  - If a subordinate completed a task, accept the result — do NOT re-dispatch
619
630
  - If the result is unsatisfactory, re-dispatch with SPECIFIC different instructions
620
- - Track dispatched job IDs — never repeat the same task
631
+ - Track dispatched session IDs — never repeat the same task
621
632
  - After 2 dispatches to the same role, accept the result or report to CEO
622
633
 
623
634
  **Example: Full supervision session**
624
635
  \`\`\`bash
625
636
  # Task 1: Dispatch to engineer
626
637
  python3 "$DISPATCH_CMD" engineer "Implement feature X. Read tasks.md first."
627
- # → Job ID: job-001
638
+ # → Session ID: ses-001
639
+
640
+ # Supervision watch (blocks 120s, returns digest)
641
+ python3 "$SUPERVISION_CMD" watch ses-001 --duration 120
642
+ # → Digest: Engineer creating auth module (significance: 3, no anomalies)
643
+ # Judgment: On track, continue watching
628
644
 
629
- # Poll until done
630
- python3 "$DISPATCH_CMD" --check job-001
631
- # → Status: RUNNINGcheck again in 10-30s
632
- python3 "$DISPATCH_CMD" --check job-001
633
- # → Status: DONE (result printed)
645
+ python3 "$SUPERVISION_CMD" watch ses-001 --duration 120
646
+ # Digest: Engineer importing external API directly (significance: 7, anomaly: scope_creep)
647
+ # Judgment: Wrong approach amend!
648
+ python3 "$SUPERVISION_CMD" amend ses-001 "Use our adapter pattern, not direct API import"
649
+
650
+ python3 "$SUPERVISION_CMD" watch ses-001 --duration 120
651
+ # → Digest: Engineer completed (msg:done)
634
652
 
635
653
  # Review result... looks good. Task 2 (QA):
636
654
  python3 "$DISPATCH_CMD" qa "Test feature X that engineer just implemented."
637
- # → Job ID: job-002
638
- python3 "$DISPATCH_CMD" --check job-002
639
- # → Status: DONE found bugs
655
+ # → Session ID: ses-002
656
+ python3 "$SUPERVISION_CMD" watch ses-002 --duration 120
657
+ # → Digest: QA found bugs (significance: 6)
640
658
 
641
659
  # Re-dispatch with SPECIFIC fix:
642
660
  python3 "$DISPATCH_CMD" engineer "Fix BUG: null check missing in auth.ts line 42"
643
- # → Job ID: job-003
644
- python3 "$DISPATCH_CMD" --check job-003
645
- # → Status: DONE — all good. Update knowledge and report.
661
+ # → Session ID: ses-003
662
+ python3 "$SUPERVISION_CMD" watch ses-003 --duration 120
663
+ # → DONE — all good. Update knowledge and report.
646
664
  \`\`\`
647
665
 
648
666
  ⚠️ Do NOT use curl or other methods to create jobs — always use the dispatch command.
@@ -707,53 +725,110 @@ function buildSupervisionSection(node: OrgNode): string {
707
725
  const hb = node.heartbeat ?? { enabled: true, intervalSec: 120, maxTicks: 60 };
708
726
  return `# Supervision Mode (Heartbeat)
709
727
 
710
- ⛔ **When you dispatch subordinates, you MUST enter supervision mode using heartbeat_watch.**
711
- ⛔ **Do NOT use sleep+curl polling. heartbeat_watch blocks server-side at zero cost.**
728
+ ⛔ **After dispatching subordinates, you MUST enter supervision mode.**
729
+ ⛔ **Use \`python3 "$SUPERVISION_CMD" watch\` it blocks server-side at zero LLM cost and returns a digest.**
730
+ ⛔ **Do NOT use sleep + \`--check\` polling. The supervision watch command is cheaper and gives better information.**
712
731
 
713
732
  ## Supervision Protocol
714
733
 
715
- 1. **Dispatch** subordinates with clear task descriptions
716
- 2. **Call heartbeat_watch** with the returned session IDs:
717
- \`heartbeat_watch(sessionIds=[...], durationSec=${hb.intervalSec})\`
718
- 3. **Analyze the digest** against your plan:
719
- - On track → call heartbeat_watch again (keep watching)
720
- - Off track \`amend_session(sessionId, instruction)\` to course-correct
721
- - Seriously wrong \`abort_session(sessionId)\` + re-dispatch with different instructions
722
- - Need peer input \`consult(peer_role_id, question)\`
723
- - All done compile results and report to your superior
724
- 4. **Repeat** heartbeat_watch until all subordinates complete
725
-
726
- ## Available Supervision Tools
727
-
728
- | Tool | When to Use |
729
- |------|-------------|
730
- | \`heartbeat_watch\` | Watch subordinate sessions (blocks ${hb.intervalSec}s, $0 LLM cost) |
731
- | \`amend_session\` | Inject new instructions into a running session |
732
- | \`abort_session\` | Kill a session that's going wrong |
733
- | \`consult\` | Ask a peer C-Level for their perspective |
734
+ 1. **Dispatch** subordinates with clear task descriptions. Save returned session IDs.
735
+ 2. **Watch** with the supervision command:
736
+ \`\`\`bash
737
+ python3 "$SUPERVISION_CMD" watch <ses-id1>,<ses-id2> --duration ${hb.intervalSec}
738
+ \`\`\`
739
+ This blocks for ${hb.intervalSec}s (or until an alert event) and returns a digest summary.
740
+ 3. **Analyze the digest** each tick, you must think critically:
741
+ - **Direction check**: Are subordinates on track with the plan?
742
+ - **Quality check**: Is the approach correct? (e.g., right patterns, right tools)
743
+ - **Peer consult**: Unsure about business/market direction? → \`python3 "$CONSULT_CMD" cbo "question"\`
744
+ - ⚠️ **Course correct**: Wrong direction → \`python3 "$SUPERVISION_CMD" amend <ses-id> "new instruction"\`
745
+ - 🛑 **Abort**: Seriously wrong → \`python3 "$SUPERVISION_CMD" abort <ses-id> --reason "why"\`
746
+ - ✅ **All done**: Compile results and report to your superior
747
+ 4. **Repeat** watch until all subordinates complete. Do NOT stop after one tick.
748
+
749
+ ## Supervision Commands
750
+
751
+ | Command | Description |
752
+ |---------|-------------|
753
+ | \`python3 "$SUPERVISION_CMD" watch <ids> --duration ${hb.intervalSec}\` | Watch sessions (blocks ${hb.intervalSec}s, returns digest) |
754
+ | \`python3 "$SUPERVISION_CMD" amend <ses-id> "instruction"\` | Inject new instructions into running session |
755
+ | \`python3 "$SUPERVISION_CMD" abort <ses-id> --reason "why"\` | Kill a session going wrong |
756
+ | \`python3 "$SUPERVISION_CMD" peers --wave <waveId> --role <roleId>\` | Discover peer C-Level sessions |
757
+ | \`python3 "$CONSULT_CMD" <peer-role> "question"\` | Ask a peer C-Level for input |
734
758
 
735
759
  ## Digest Response
736
760
 
737
- heartbeat_watch returns a digest with:
738
- - **Significance score** (0-10): How much attention this tick needs
739
- - **Anomalies**: Errors, stalls (3min+), sessions awaiting input
740
- - **Per-session activity**: What each subordinate has been doing
741
- - **Peer activity** (if peers are also in supervision mode)
761
+ The watch command returns a JSON digest with:
762
+ - **significanceScore** (0-10): How much attention this tick needs
763
+ - **anomalies**: Errors, stalls (3min+ no events), sessions awaiting input
764
+ - **text**: Human-readable summary of per-session activity
765
+
766
+ Score 0-1 = quiet (all normal). Score 5+ = needs attention. Score 8+ = likely needs intervention.
767
+
768
+ ## Tick Quality (CRITICAL)
742
769
 
743
- Quiet ticks (score 0-1) return a single line: "All N sessions progressing normally."
770
+ **Each tick, you must make a REAL judgment not just "still running, continuing".**
771
+
772
+ Good tick response:
773
+ \`\`\`
774
+ Digest: Engineer working on auth module (seq 45), QA waiting.
775
+ Judgment: Engineer is importing external API directly — should use our adapter pattern.
776
+ Action: amend engineer session with instruction to use adapter.
777
+ \`\`\`
778
+
779
+ Bad tick response (DO NOT DO THIS):
780
+ \`\`\`
781
+ "Sessions are still running. Continuing to watch."
782
+ \`\`\`
744
783
 
745
784
  ## Budget
746
785
 
747
786
  - Max ticks: ${hb.maxTicks} (${Math.round(hb.maxTicks * hb.intervalSec / 60)} minutes total)
748
- - Quiet tick cost: ~$0.001 (minimal LLM analysis)
749
- - Alert tick cost: ~$0.02-0.05 (intervention decision)
787
+ - Quiet tick cost: ~$0.001 | Alert tick cost: ~$0.02-0.05
750
788
 
751
789
  ## ⛔ Anti-Patterns
752
790
 
753
- - ❌ Using \`bash_execute\` with sleep/curl to poll — use heartbeat_watch instead
754
- - ❌ Calling \`--check\` in a loop heartbeat_watch handles this automatically
791
+ - ❌ Using \`sleep\` + \`--check\` to poll — use supervision watch instead
792
+ - ❌ Saying "still RUNNING, continuing to wait" without real analysis
755
793
  - ❌ Ignoring digest anomalies — always address errors and stalls
756
- - ❌ Not re-watching after a quiet tick — keep the loop going until all done`;
794
+ - ❌ Stopping supervision after one tick — keep watching until ALL done
795
+ - ❌ Not consulting peers when business/market judgment is needed`;
796
+ }
797
+
798
+ function buildKnowledgeManagementSection(roleId: string): string {
799
+ const domainScope = roleId === 'cto'
800
+ ? '`architecture/`, `knowledge/` (technical docs)'
801
+ : roleId === 'cbo'
802
+ ? '`knowledge/`, `company/` (business docs)'
803
+ : '`knowledge/` (your domain docs)';
804
+
805
+ return `# Knowledge Consistency Management (C-Level Responsibility)
806
+
807
+ ⛔ **You are responsible for keeping your domain's knowledge current and consistent.**
808
+
809
+ ## When to Check
810
+
811
+ - After completing any task (The Loop step ④)
812
+ - During supervision ticks when subordinates are progressing normally
813
+ - When you notice conflicting information during work
814
+
815
+ ## Knowledge Gate
816
+
817
+ 1. Check if changes conflict with existing documents (grep 3+ keywords)
818
+ 2. Update or deprecate stale references — do NOT leave legacy docs pretending to be current
819
+ 3. Verify new docs are registered in their Hub
820
+ 4. Ensure cross-links between related documents
821
+
822
+ ## AKB Linting
823
+
824
+ | Check | Description |
825
+ |-------|-------------|
826
+ | Orphan docs | Every document reachable from a Hub? |
827
+ | Stale content | Deprecated designs still described as current? Mark ⚠️ |
828
+ | Term consistency | Same concept called different names across docs? |
829
+ | Date freshness | Old dates/status without warning? |
830
+
831
+ **Your scope**: ${domainScope}`;
757
832
  }
758
833
 
759
834
  function buildConsultSection(orgTree: OrgTree, roleId: string): string | null {
@@ -30,7 +30,7 @@ export interface HeartbeatConfig {
30
30
  export interface OrgNode {
31
31
  id: string;
32
32
  name: string;
33
- level: 'c-level' | 'team-lead' | 'member';
33
+ level: 'c-level' | 'member';
34
34
  reportsTo: string;
35
35
  children: string[];
36
36
  persona: string;
@@ -19,7 +19,7 @@ export interface SkillExportDef {
19
19
  export interface RoleDefinition {
20
20
  id: string;
21
21
  name: string;
22
- level: 'c-level' | 'team-lead' | 'member';
22
+ level: 'c-level' | 'member';
23
23
  reportsTo: string;
24
24
  persona: string;
25
25
  skills?: string[];