BMAD-METHOD/.claude/orchestrator/feedback-loop-engine.md

17 KiB
Raw Blame History

Feedback Loop Engine - Adaptive Workflow Coordination

Overview

The Feedback Loop Engine enables bidirectional communication between agents, allowing downstream agents to notify upstream agents of constraints, inconsistencies, or issues discovered during execution. This creates an adaptive workflow that can self-correct and optimize based on real-world implementation findings.


Core Concept

Traditional workflow: Analyst → PM → Architect → Developer → QA (one-way)

Feedback-enabled workflow:

    ┌─────────────────────────────────────┐
    │                                     │
    ▼                                     │
Analyst → PM → Architect → Developer → QA
    ▲        ▲        ▲          ▲
    │        │        │          │
    └────────┴────────┴──────────┘
    (feedback notifications)

Feedback Mechanisms

1. Constraint Backpropagation

Scenario: Developer discovers implementation constraint that affects architecture

constraint_backpropagation:
  trigger:
    agent: developer
    event: "implementation_constraint_discovered"

  examples:
    - constraint: "hosting doesn't support WebSockets"
      requirement_id: "REQ-123"
      affected_agents: ["architect", "pm"]

    - constraint: "database performance insufficient"
      requirement_id: "REQ-045"
      affected_agents: ["architect"]

    - constraint: "third-party API rate limit too restrictive"
      requirement_id: "REQ-078"
      affected_agents: ["architect", "pm"]

  action:
    - pause_workflow: true
    - notify_agents: ["architect", "pm"]
    - request_decision:
        options:
          - "revise_architecture"
          - "change_hosting_provider"
          - "revise_requirement"
          - "accept_limitation"
    - resume_on: "decision_made"

Implementation Pattern:

// During developer agent execution
if (implementationConstraintFound) {
  await feedbackLoop.notifyConstraint({
    source: "developer",
    constraint: {
      type: "technical_limitation",
      description: "WebSocket not supported by current hosting",
      affected_requirement: "REQ-123",
      severity: "blocking"
    },
    request: {
      targets: ["architect", "pm"],
      action_required: "architecture_revision_or_requirement_change",
      options: [
        {
          option: "switch_to_polling",
          impact: "performance_degradation",
          effort: "low"
        },
        {
          option: "change_hosting_provider",
          impact: "deployment_complexity",
          effort: "high"
        },
        {
          option: "revise_realtime_requirement",
          impact: "feature_reduction",
          effort: "medium"
        }
      ]
    }
  });

  // Wait for resolution
  const resolution = await feedbackLoop.waitForResolution("REQ-123");

  // Continue with updated context
  await implementWithResolution(resolution);
}

2. Validation Failure Callbacks

Scenario: Architect validates PM requirements and finds technical infeasibility

validation_failure_callbacks:
  architect_validates_pm:
    trigger: "architect finds technical infeasibility"

    examples:
      - finding: "requirement X requires technology not in approved stack"
        requirement_id: "REQ-056"

      - finding: "performance requirement Y is not achievable with given constraints"
        requirement_id: "REQ-089"

    action:
      - notify_pm:
          message: "Technical validation failed for requirement X"
          details: "architect's analysis and reasoning"
      - request_revision: true
      - preserve_context:
          - "architect's technical analysis"
          - "alternative approaches evaluated"
      - re_execute:
          - agent: "pm"
          - step: 2
          - context: "with_architect_feedback"

  qa_validates_implementation:
    trigger: "qa finds missing functionality"

    examples:
      - finding: "acceptance criteria not met"
        requirement_id: "REQ-012"
        test_case: "TC-045"

      - finding: "non-functional requirement violated"
        requirement_id: "REQ-067"
        metric: "response_time > 2s (target: 500ms)"

    action:
      - trace_to_requirement: true
      - identify_gap:
          compare:
            - "pm.requirements"
            - "developer.implementation"
      - escalate_to: ["pm", "developer"]
      - decision:
          options:
            - "add_missing_requirement"
            - "fix_implementation"
            - "mark_as_limitation"
            - "revise_acceptance_criteria"

3. Inconsistency Detection

Scenario: Multiple agents produce conflicting outputs

inconsistency_detection:
  ux_architect_mismatch:
    detection:
      compare:
        - "ux_expert.ui_spec.design_system.breakpoints"
        - "architect.system_architecture.responsive_strategy"

    conflicts:
      - ux_expert: "mobile_first with 320px base"
        architect: "desktop_primary with 1024px base"
        severity: "high"

    resolution:
      - notify_both: ["ux_expert", "architect"]
      - request_alignment:
          method: "consensus_meeting"
          facilitator: "orchestrator"
      - update_both_artifacts: true

  pm_architect_constraint_mismatch:
    detection:
      compare:
        - "pm.prd.technical_constraints"
        - "architect.system_architecture.selected_technologies"

    conflicts:
      - pm: "must support IE11"
        architect: "selected React 18 (no IE11 support)"
        severity: "critical"

    resolution:
      - pause_workflow: true
      - notify_agents: ["pm", "architect"]
      - request_resolution:
          options:
            - "drop_ie11_requirement"
            - "use_polyfills"
            - "downgrade_react_version"
            - "accept_dual_implementation"
      - resume_on: "consensus_reached"

4. Quality Gate Feedback

Scenario: Quality gate fails and needs upstream attention

quality_gate_feedback:
  accessibility_failure:
    gate: "WCAG_AA_compliance"
    threshold: "zero_violations"
    actual: "12_violations"

    action:
      - notify_ux_expert:
          message: "Accessibility violations detected"
          details: "12 WCAG AA violations in components"
          violations: ["color_contrast", "keyboard_navigation", "aria_labels"]
      - request_fix:
          target: "ux_expert"
          artifact: "ui_spec"
          specific_issues: [...violations]
      - block_developer_step: true
      - resume_on: "ux_spec_revised"

  performance_failure:
    gate: "performance_budget"
    threshold: "lighthouse_score >= 90"
    actual: "lighthouse_score = 67"

    action:
      - trace_root_cause:
          - "bundle_size: 2.5MB (target: 500KB)"
          - "unoptimized_images: 15 files"
          - "blocking_scripts: 8 files"
      - notify_agents: ["architect", "developer", "ux_expert"]
      - request_optimization:
          - architect: "review_technology_choices"
          - developer: "implement_code_splitting"
          - ux_expert: "optimize_image_assets"
      - re_validate_on: "all_fixes_applied"

Implementation Architecture

Feedback Loop State Machine

feedback_loop_states:
  IDLE:
    description: "No active feedback loops"
    transitions:
      - event: "feedback_triggered"
        next_state: "NOTIFYING"

  NOTIFYING:
    description: "Sending notifications to target agents"
    actions:
      - create_feedback_record
      - send_notifications
      - update_context_bus
    transitions:
      - event: "notifications_sent"
        next_state: "WAITING_RESPONSE"

  WAITING_RESPONSE:
    description: "Waiting for agent acknowledgment and action"
    timeout: "10_minutes"
    actions:
      - poll_for_acknowledgment
      - track_response_time
    transitions:
      - event: "response_received"
        next_state: "RESOLVING"
      - event: "timeout"
        next_state: "ESCALATING"

  RESOLVING:
    description: "Agents are working on resolution"
    actions:
      - monitor_progress
      - coordinate_updates
      - validate_resolution
    transitions:
      - event: "resolution_complete"
        next_state: "VALIDATING"
      - event: "resolution_failed"
        next_state: "ESCALATING"

  VALIDATING:
    description: "Validating the resolution"
    actions:
      - run_validation_checks
      - verify_consistency
      - update_artifacts
    transitions:
      - event: "validation_passed"
        next_state: "RESOLVED"
      - event: "validation_failed"
        next_state: "ESCALATING"

  RESOLVED:
    description: "Feedback loop successfully resolved"
    actions:
      - update_context
      - log_resolution
      - resume_workflow
    transitions:
      - event: "new_feedback"
        next_state: "NOTIFYING"
      - event: "workflow_complete"
        next_state: "IDLE"

  ESCALATING:
    description: "Escalating to human or system administrator"
    actions:
      - create_escalation_ticket
      - notify_administrators
      - pause_workflow
    transitions:
      - event: "manual_resolution"
        next_state: "RESOLVED"

Context Bus Integration

// Feedback loop uses context bus for coordination
class FeedbackLoopEngine {
  constructor(contextBus) {
    this.contextBus = contextBus;
    this.activeLoops = new Map();

    // Subscribe to agent completion events
    this.contextBus.subscribe('agent_contexts.*.status', (newStatus, oldStatus, path) => {
      if (newStatus === 'completed') {
        this.onAgentCompleted(path);
      }
    });
  }

  /**
   * Trigger feedback loop
   */
  async trigger(feedbackConfig) {
    const loopId = `loop-${Date.now()}`;

    // Create feedback loop record
    const loop = {
      id: loopId,
      triggered_at: new Date().toISOString(),
      source_agent: feedbackConfig.source,
      target_agents: feedbackConfig.targets,
      issue_type: feedbackConfig.type,
      description: feedbackConfig.description,
      severity: feedbackConfig.severity,
      status: 'pending',
      state: 'NOTIFYING'
    };

    // Store in context
    this.contextBus.push('feedback_loops', loop);
    this.activeLoops.set(loopId, loop);

    // Notify target agents
    await this.notifyAgents(loop);

    return loopId;
  }

  /**
   * Notify target agents
   */
  async notifyAgents(loop) {
    for (const targetAgent of loop.target_agents) {
      const notification = {
        from_agent: loop.source_agent,
        type: loop.issue_type,
        message: loop.description,
        severity: loop.severity,
        resolved: false,
        loop_id: loop.id
      };

      this.contextBus.push(
        `agent_contexts.${targetAgent}.feedback_received`,
        notification
      );
    }

    // Update state
    loop.state = 'WAITING_RESPONSE';
    this.contextBus.update(`feedback_loops.${loop.id}`, { state: 'WAITING_RESPONSE' });
  }

  /**
   * Wait for resolution
   */
  async waitForResolution(loopId, timeout = 600000) {
    return new Promise((resolve, reject) => {
      const startTime = Date.now();

      const checkInterval = setInterval(() => {
        const loop = this.activeLoops.get(loopId);

        if (!loop) {
          clearInterval(checkInterval);
          reject(new Error(`Loop not found: ${loopId}`));
          return;
        }

        if (loop.status === 'resolved') {
          clearInterval(checkInterval);
          resolve(loop.resolution);
          return;
        }

        if (Date.now() - startTime > timeout) {
          clearInterval(checkInterval);
          this.escalate(loopId, 'timeout');
          reject(new Error(`Feedback loop timeout: ${loopId}`));
        }
      }, 1000);
    });
  }

  /**
   * Resolve feedback loop
   */
  async resolve(loopId, resolution) {
    const loop = this.activeLoops.get(loopId);

    if (!loop) {
      throw new Error(`Loop not found: ${loopId}`);
    }

    loop.status = 'resolved';
    loop.state = 'RESOLVED';
    loop.resolution = resolution;
    loop.resolved_at = new Date().toISOString();

    // Update context
    this.contextBus.update(`feedback_loops.${loop.id}`, {
      status: 'resolved',
      state: 'RESOLVED',
      resolution: resolution,
      resolved_at: loop.resolved_at
    });

    // Mark notifications as resolved
    for (const targetAgent of loop.target_agents) {
      const feedbacks = this.contextBus.get(`agent_contexts.${targetAgent}.feedback_received`) || [];
      for (const feedback of feedbacks) {
        if (feedback.loop_id === loopId) {
          feedback.resolved = true;
        }
      }
    }

    // Remove from active loops
    this.activeLoops.delete(loopId);

    return resolution;
  }

  /**
   * Escalate unresolved loop
   */
  async escalate(loopId, reason) {
    const loop = this.activeLoops.get(loopId);

    if (!loop) return;

    loop.status = 'escalated';
    loop.state = 'ESCALATING';
    loop.escalation_reason = reason;

    this.contextBus.update(`feedback_loops.${loop.id}`, {
      status: 'escalated',
      state: 'ESCALATING',
      escalation_reason: reason
    });

    // Pause workflow
    this.contextBus.set('workflow_state.paused', true);
    this.contextBus.set('workflow_state.pause_reason', `Feedback loop escalation: ${loopId}`);

    console.error(`\n⚠  Feedback loop escalated: ${loopId}`);
    console.error(`   Reason: ${reason}`);
    console.error(`   Issue: ${loop.description}`);
    console.error(`   Manual intervention required.\n`);
  }
}

Usage Examples

Example 1: Developer finds WebSocket constraint

// In developer agent
const feedbackLoop = new FeedbackLoopEngine(contextBus);

const loopId = await feedbackLoop.trigger({
  source: 'developer',
  targets: ['architect', 'pm'],
  type: 'constraint_violation',
  severity: 'blocking',
  description: 'WebSocket not supported by current hosting (Vercel)',
  details: {
    requirement_id: 'REQ-123',
    requirement_text: 'Real-time collaboration with WebSocket',
    constraint: 'Vercel serverless functions do not support persistent WebSocket connections',
    options: [
      { option: 'Use polling', effort: 'low', impact: 'performance_degradation' },
      { option: 'Switch to AWS with EC2', effort: 'high', impact: 'deployment_change' },
      { option: 'Use third-party service (Pusher)', effort: 'medium', impact: 'cost_increase' }
    ]
  }
});

// Wait for architect and PM to decide
const resolution = await feedbackLoop.waitForResolution(loopId);

console.log(`Resolution: ${resolution.decision}`);
// Implement based on resolution

Example 2: Architect finds performance infeasibility

// In architect agent
const feedbackLoop = new FeedbackLoopEngine(contextBus);

const loopId = await feedbackLoop.trigger({
  source: 'architect',
  targets: ['pm'],
  type: 'technical_infeasibility',
  severity: 'high',
  description: 'Performance requirement not achievable with current constraints',
  details: {
    requirement_id: 'REQ-089',
    requirement_text: 'Page load time < 500ms',
    analysis: 'With 50MB of data to load and 3G mobile network (750Kbps), minimum load time is 2.5 seconds',
    recommendation: 'Revise requirement to < 2 seconds OR reduce initial data load'
  }
});

const resolution = await feedbackLoop.waitForResolution(loopId);

Example 3: QA finds missing functionality

// In QA agent
const feedbackLoop = new FeedbackLoopEngine(contextBus);

const loopId = await feedbackLoop.trigger({
  source: 'qa',
  targets: ['pm', 'developer'],
  type: 'missing_requirement',
  severity: 'medium',
  description: 'User story acceptance criteria not met',
  details: {
    user_story_id: 'US-045',
    acceptance_criteria: 'User can export data as CSV',
    actual_implementation: 'Only JSON export is implemented',
    gap: 'CSV export functionality missing'
  }
});

const resolution = await feedbackLoop.waitForResolution(loopId);

Benefits

  1. Adaptive Workflows: Workflows can self-correct based on real-world findings
  2. Reduced Rework: Issues discovered early through validation callbacks
  3. Better Alignment: Inconsistencies detected and resolved automatically
  4. Transparent Decision-Making: All feedback and resolutions logged in context
  5. Improved Quality: Multiple validation passes ensure consistency

Integration with Parallel Execution

Feedback loops work seamlessly with parallel execution:

parallel_execution_with_feedback:
  # Architect and UX Expert run in parallel
  - group: design_and_architecture
    parallel: true
    agents: [ux-expert, architect]

  # On completion, check for inconsistencies
  - on_group_complete:
      run: consistency_check
      compare:
        - ux-expert.outputs.design_system
        - architect.outputs.technology_stack

      if: inconsistencies_found
      then:
        trigger_feedback_loop:
          source: orchestrator
          targets: [ux-expert, architect]
          type: inconsistency

  # Wait for resolution before proceeding
  - next_group: implementation
    depends_on: [design_and_architecture, feedback_loops_resolved]

Conclusion

The Feedback Loop Engine transforms BMAD-SPEC-KIT from a one-directional pipeline into an adaptive, self-correcting system that can handle real-world complexity and constraints discovered during execution.