diff --git a/apps/web/src/lib/code-reviews/prompts/council-prompt.test.ts b/apps/web/src/lib/code-reviews/prompts/council-prompt.test.ts index c25f9b210b..cc54f2796e 100644 --- a/apps/web/src/lib/code-reviews/prompts/council-prompt.test.ts +++ b/apps/web/src/lib/code-reviews/prompts/council-prompt.test.ts @@ -98,6 +98,27 @@ describe('buildCouncilOrchestratorPrompt', () => { // Coordinator must not compute the decision itself. expect(prompt.toLowerCase()).toContain('do not compute an overall decision'); }); + + it('instructs the coordinator to narrate progress (startup, per-specialist, done)', () => { + const prompt = buildCouncilOrchestratorPrompt({ + basePrompt: 'BASE', + specialists: [ + specialist({ id: 'security', name: 'Security' }), + specialist({ id: 'performance', name: 'Performance', role: 'performance' }), + ], + aggregationStrategy: 'majority', + }); + + // 1. Startup line names the specialists AND the formatted governance label — assert the + // full fragment so it isn't satisfied by the separate `describeAggregationStrategy` text. + expect(prompt).toContain( + 'Starting council review with 2 specialists (Security, Performance) using Majority governance.' + ); + // 2. Per-specialist start. 3. All-done. 4. Done message. + expect(prompt).toContain('Starting review...'); + expect(prompt).toContain('All specialists complete'); + expect(prompt).toContain('Council review complete.'); + }); }); describe('buildSpecialistAgentPrompt', () => { diff --git a/apps/web/src/lib/code-reviews/prompts/council-prompt.ts b/apps/web/src/lib/code-reviews/prompts/council-prompt.ts index 168ae19ab3..bde081f5b1 100644 --- a/apps/web/src/lib/code-reviews/prompts/council-prompt.ts +++ b/apps/web/src/lib/code-reviews/prompts/council-prompt.ts @@ -5,6 +5,7 @@ import type { RuntimeAgentInput } from '@kilocode/worker-utils/cloud-agent-next- import { COUNCIL_RESULT_MARKER_TAG, describeAggregationStrategy, + formatAggregationStrategy, } from '@kilocode/worker-utils/code-review-council'; /** @@ -56,6 +57,7 @@ export function buildCouncilOrchestratorPrompt(params: { const roster = specialists .map(s => `- subagent_type "${s.id}" — ${s.name}: ${s.lens}`) .join('\n'); + const specialistNames = specialists.map(s => s.name).join(', '); const coordination = [ '# COUNCIL MODE', @@ -69,6 +71,15 @@ export function buildCouncilOrchestratorPrompt(params: { '', `Governance (for context only — our system computes the final decision): ${describeAggregationStrategy(aggregationStrategy)}`, '', + // A council coordinator is mostly quiet (it delegates and waits), so the live session log + // looks empty and the run appears stuck. Narrate progress so the operator sees it working. + 'Progress updates — print each of the following as a plain-text status line on its own', + 'line, at the moment you reach it, so the run shows live progress in the session log:', + `- At the very start, before delegating: "Starting council review with ${specialists.length} specialists (${specialistNames}) using ${formatAggregationStrategy(aggregationStrategy)} governance."`, + '- Immediately before you start each specialist, name it: "Starting review..."', + '- Once every specialist has reported back: "All specialists complete. Aggregating results..."', + '- When you are finished (right before the manifest below): "Council review complete."', + '', 'When every specialist has reported, emit EXACTLY ONE machine-readable manifest on its', 'own line in your final message, verbatim on a line by itself. It does NOT need to be', 'the very last line — if any other trailing markers are required, they may follow it:',