Class: Aidp::Harness::ConfigSchema

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/harness/config_schema.rb

Overview

Configuration schema and validation for harness

Constant Summary collapse

SCHEMA =

Define the complete configuration schema

{
  harness: {
    type: :hash,
    required: true,
    properties: {
      max_retries: {
        type: :integer,
        required: false,
        default: 2,
        min: 0,
        max: 10
      },
      default_provider: {
        type: :string,
        required: true,
        pattern: /^[a-zA-Z0-9_-]+$/
      },
      fallback_providers: {
        type: :array,
        required: false,
        default: [],
        items: {
          type: :string,
          pattern: /^[a-zA-Z0-9_-]+$/
        }
      },
      no_api_keys_required: {
        type: :boolean,
        required: false,
        default: false
      },
      provider_weights: {
        type: :hash,
        required: false,
        default: {},
        pattern_properties: {
          /^[a-zA-Z0-9_-]+$/ => {
            type: :integer,
            min: 1,
            max: 10
          }
        }
      },
      circuit_breaker: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          failure_threshold: 5,
          timeout: 300,
          half_open_max_calls: 3
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          failure_threshold: {
            type: :integer,
            required: false,
            default: 5,
            min: 1,
            max: 100
          },
          timeout: {
            type: :integer,
            required: false,
            default: 300,
            min: 60,
            max: 3600
          },
          half_open_max_calls: {
            type: :integer,
            required: false,
            default: 3,
            min: 1,
            max: 10
          }
        }
      },
      retry: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          max_attempts: 3,
          base_delay: 1.0,
          max_delay: 60.0,
          exponential_base: 2.0,
          jitter: true
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          max_attempts: {
            type: :integer,
            required: false,
            default: 3,
            min: 1,
            max: 10
          },
          base_delay: {
            type: :number,
            required: false,
            default: 1.0,
            min: 0.1,
            max: 60.0
          },
          max_delay: {
            type: :number,
            required: false,
            default: 60.0,
            min: 1.0,
            max: 3600.0
          },
          exponential_base: {
            type: :number,
            required: false,
            default: 2.0,
            min: 1.1,
            max: 5.0
          },
          jitter: {
            type: :boolean,
            required: false,
            default: true
          }
        }
      },
      rate_limit: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          default_reset_time: 3600,
          burst_limit: 10,
          sustained_limit: 5
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          default_reset_time: {
            type: :integer,
            required: false,
            default: 3600,
            min: 60,
            max: 86400
          },
          burst_limit: {
            type: :integer,
            required: false,
            default: 10,
            min: 1,
            max: 1000
          },
          sustained_limit: {
            type: :integer,
            required: false,
            default: 5,
            min: 1,
            max: 100
          }
        }
      },
      load_balancing: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          strategy: "weighted_round_robin",
          health_check_interval: 30,
          unhealthy_threshold: 3
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          strategy: {
            type: :string,
            required: false,
            default: "weighted_round_robin",
            enum: ["round_robin", "weighted_round_robin", "least_connections", "random"]
          },
          health_check_interval: {
            type: :integer,
            required: false,
            default: 30,
            min: 10,
            max: 300
          },
          unhealthy_threshold: {
            type: :integer,
            required: false,
            default: 3,
            min: 1,
            max: 10
          }
        }
      },
      model_switching: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          auto_switch_on_error: true,
          auto_switch_on_rate_limit: true,
          fallback_strategy: "sequential"
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          auto_switch_on_error: {
            type: :boolean,
            required: false,
            default: true
          },
          auto_switch_on_rate_limit: {
            type: :boolean,
            required: false,
            default: true
          },
          fallback_strategy: {
            type: :string,
            required: false,
            default: "sequential",
            enum: ["sequential", "random", "weighted"]
          }
        }
      },
      health_check: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          interval: 60,
          timeout: 10,
          failure_threshold: 3,
          success_threshold: 2
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          interval: {
            type: :integer,
            required: false,
            default: 60,
            min: 10,
            max: 600
          },
          timeout: {
            type: :integer,
            required: false,
            default: 10,
            min: 1,
            max: 60
          },
          failure_threshold: {
            type: :integer,
            required: false,
            default: 3,
            min: 1,
            max: 10
          },
          success_threshold: {
            type: :integer,
            required: false,
            default: 2,
            min: 1,
            max: 5
          }
        }
      },
      metrics: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          retention_days: 30,
          aggregation_interval: 300,
          export_interval: 3600
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          retention_days: {
            type: :integer,
            required: false,
            default: 30,
            min: 1,
            max: 365
          },
          aggregation_interval: {
            type: :integer,
            required: false,
            default: 300,
            min: 60,
            max: 3600
          },
          export_interval: {
            type: :integer,
            required: false,
            default: 3600,
            min: 300,
            max: 86400
          }
        }
      },
      session: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          timeout: 1800,
          sticky_sessions: true,
          session_affinity: "provider_model"
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          timeout: {
            type: :integer,
            required: false,
            default: 1800,
            min: 300,
            max: 7200
          },
          sticky_sessions: {
            type: :boolean,
            required: false,
            default: true
          },
          session_affinity: {
            type: :string,
            required: false,
            default: "provider_model",
            enum: ["provider_model", "provider", "model", "none"]
          }
        }
      },
      work_loop: {
        type: :hash,
        required: false,
        default: {
          enabled: true,
          max_iterations: 50,
          test_commands: [],
          lint_commands: [],
          formatter_commands: [],
          build_commands: [],
          documentation_commands: [],
          units: {},
          guards: {enabled: false},
          version_control: {tool: "git", behavior: "nothing", conventional_commits: false},
          coverage: {enabled: false},
          interactive_testing: {enabled: false, app_type: "web"}
        },
        properties: {
          enabled: {
            type: :boolean,
            required: false,
            default: true
          },
          max_iterations: {
            type: :integer,
            required: false,
            default: 50,
            min: 1,
            max: 200
          },
          test_commands: {
            type: :array,
            required: false,
            default: []
            # Items can be strings or {command: string, required: boolean}
            # Validation handled in Configuration class for flexibility
          },
          lint_commands: {
            type: :array,
            required: false,
            default: []
            # Items can be strings or {command: string, required: boolean}
            # Validation handled in Configuration class for flexibility
          },
          formatter_commands: {
            type: :array,
            required: false,
            default: []
            # Items can be strings or {command: string, required: boolean}
            # Validation handled in Configuration class for flexibility
          },
          build_commands: {
            type: :array,
            required: false,
            default: []
            # Items can be strings or {command: string, required: boolean}
            # Validation handled in Configuration class for flexibility
          },
          documentation_commands: {
            type: :array,
            required: false,
            default: []
            # Items can be strings or {command: string, required: boolean}
            # Validation handled in Configuration class for flexibility
          },
          units: {
            type: :hash,
            required: false,
            default: {},
            properties: {
              deterministic: {
                type: :array,
                required: false,
                default: [],
                items: {
                  type: :hash,
                  properties: {
                    name: {
                      type: :string,
                      required: true
                    },
                    command: {
                      type: :string,
                      required: false
                    },
                    type: {
                      type: :string,
                      required: false,
                      enum: ["command", "wait"]
                    },
                    output_file: {
                      type: :string,
                      required: false
                    },
                    enabled: {
                      type: :boolean,
                      required: false,
                      default: true
                    },
                    min_interval_seconds: {
                      type: :integer,
                      required: false,
                      min: 1
                    },
                    max_backoff_seconds: {
                      type: :integer,
                      required: false,
                      min: 1
                    },
                    backoff_multiplier: {
                      type: :number,
                      required: false,
                      min: 1.0
                    },
                    metadata: {
                      type: :hash,
                      required: false,
                      default: {}
                    },
                    next: {
                      type: :hash,
                      required: false,
                      default: {}
                    }
                  }
                }
              },
              defaults: {
                type: :hash,
                required: false,
                default: {},
                properties: {
                  initial_unit: {
                    type: :string,
                    required: false
                  },
                  on_no_next_step: {
                    type: :string,
                    required: false
                  },
                  fallback_agentic: {
                    type: :string,
                    required: false
                  },
                  max_consecutive_deciders: {
                    type: :integer,
                    required: false,
                    min: 1,
                    max: 10
                  }
                }
              }
            }
          },
          guards: {
            type: :hash,
            required: false,
            default: {
              enabled: false
            },
            properties: {
              enabled: {
                type: :boolean,
                required: false,
                default: false
              },
              include_files: {
                type: :array,
                required: false,
                default: [],
                items: {
                  type: :string
                }
              },
              exclude_files: {
                type: :array,
                required: false,
                default: [],
                items: {
                  type: :string
                }
              },
              confirm_files: {
                type: :array,
                required: false,
                default: [],
                items: {
                  type: :string
                }
              },
              max_lines_per_commit: {
                type: :integer,
                required: false,
                min: 1,
                max: 10000
              },
              bypass: {
                type: :boolean,
                required: false,
                default: false
              }
            }
          },
          version_control: {
            type: :hash,
            required: false,
            default: {
              tool: "git",
              behavior: "nothing",
              conventional_commits: false
            },
            properties: {
              tool: {
                type: :string,
                required: false,
                default: "git",
                enum: ["git", "svn", "none"]
              },
              behavior: {
                type: :string,
                required: false,
                default: "nothing",
                enum: ["stage", "commit", "nothing"]
              },
              conventional_commits: {
                type: :boolean,
                required: false,
                default: false
              }
            }
          },
          coverage: {
            type: :hash,
            required: false,
            default: {
              enabled: false
            },
            properties: {
              enabled: {
                type: :boolean,
                required: false,
                default: false
              },
              tool: {
                type: :string,
                required: false,
                enum: ["simplecov", "nyc", "istanbul", "coverage.py", "go-cover", "jest", "other"]
              },
              run_command: {
                type: :string,
                required: false
              },
              report_paths: {
                type: :array,
                required: false,
                default: [],
                items: {
                  type: :string
                }
              },
              fail_on_drop: {
                type: :boolean,
                required: false,
                default: false
              },
              minimum_coverage: {
                type: :number,
                required: false,
                min: 0.0,
                max: 100.0
              }
            }
          },
          interactive_testing: {
            type: :hash,
            required: false,
            default: {
              enabled: false,
              app_type: "web"
            },
            properties: {
              enabled: {
                type: :boolean,
                required: false,
                default: false
              },
              app_type: {
                type: :string,
                required: false,
                default: "web",
                enum: ["web", "cli", "desktop"]
              },
              tools: {
                type: :hash,
                required: false,
                default: {},
                properties: {
                  web: {
                    type: :hash,
                    required: false,
                    default: {},
                    properties: {
                      playwright_mcp: {
                        type: :hash,
                        required: false,
                        default: {enabled: false},
                        properties: {
                          enabled: {
                            type: :boolean,
                            required: false,
                            default: false
                          },
                          run: {
                            type: :string,
                            required: false
                          },
                          specs_dir: {
                            type: :string,
                            required: false,
                            default: ".aidp/tests/web"
                          }
                        }
                      },
                      chrome_devtools_mcp: {
                        type: :hash,
                        required: false,
                        default: {enabled: false},
                        properties: {
                          enabled: {
                            type: :boolean,
                            required: false,
                            default: false
                          },
                          run: {
                            type: :string,
                            required: false
                          },
                          specs_dir: {
                            type: :string,
                            required: false,
                            default: ".aidp/tests/web"
                          }
                        }
                      }
                    }
                  },
                  cli: {
                    type: :hash,
                    required: false,
                    default: {},
                    properties: {
                      expect: {
                        type: :hash,
                        required: false,
                        default: {enabled: false},
                        properties: {
                          enabled: {
                            type: :boolean,
                            required: false,
                            default: false
                          },
                          run: {
                            type: :string,
                            required: false
                          },
                          specs_dir: {
                            type: :string,
                            required: false,
                            default: ".aidp/tests/cli"
                          }
                        }
                      }
                    }
                  },
                  desktop: {
                    type: :hash,
                    required: false,
                    default: {},
                    properties: {
                      applescript: {
                        type: :hash,
                        required: false,
                        default: {enabled: false},
                        properties: {
                          enabled: {
                            type: :boolean,
                            required: false,
                            default: false
                          },
                          run: {
                            type: :string,
                            required: false
                          },
                          specs_dir: {
                            type: :string,
                            required: false,
                            default: ".aidp/tests/desktop"
                          }
                        }
                      },
                      screen_reader: {
                        type: :hash,
                        required: false,
                        default: {enabled: false},
                        properties: {
                          enabled: {
                            type: :boolean,
                            required: false,
                            default: false
                          },
                          notes: {
                            type: :string,
                            required: false
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  thinking: {
    type: :hash,
    required: false,
    properties: {
      default_tier: {
        type: :string,
        required: false,
        default: "standard",
        enum: ["mini", "standard", "thinking", "pro", "max"]
      },
      max_tier: {
        type: :string,
        required: false,
        default: "standard",
        enum: ["mini", "standard", "thinking", "pro", "max"]
      },
      allow_provider_switch: {
        type: :boolean,
        required: false,
        default: true
      },
      escalation: {
        type: :hash,
        required: false,
        properties: {
          on_fail_attempts: {
            type: :integer,
            required: false,
            default: 2,
            min: 1,
            max: 10
          },
          on_complexity_threshold: {
            type: :hash,
            required: false,
            default: {},
            properties: {
              files_changed: {
                type: :integer,
                required: false,
                min: 1
              },
              modules_touched: {
                type: :integer,
                required: false,
                min: 1
              }
            }
          }
        }
      },
      permissions_by_tier: {
        type: :hash,
        required: false,
        default: {},
        pattern_properties: {
          /^(mini|standard|thinking|pro|max)$/ => {
            type: :string,
            enum: ["safe", "tools", "dangerous"]
          }
        }
      },
      overrides: {
        type: :hash,
        required: false,
        default: {},
        pattern_properties: {
          /^(skill|template)\..+$/ => {
            type: :string,
            enum: ["mini", "standard", "thinking", "pro", "max"]
          }
        }
      }
    }
  },
  providers: {
    type: :hash,
    required: false,
    default: {},
    pattern_properties: {
      /^[a-zA-Z0-9_-]+$/ => {
        type: :hash,
        properties: {
          type: {
            type: :string,
            required: true,
            enum: ["usage_based", "subscription", "passthrough"]
          },
          priority: {
            type: :integer,
            required: false,
            default: 1,
            min: 1,
            max: 10
          },
          model_family: {
            type: :string,
            required: false,
            default: "auto",
            enum: ["auto", "openai_o", "claude", "mistral", "local"]
          },
          max_tokens: {
            type: :integer,
            required: false,
            min: 1000,
            max: 1_000_000
          },
          default_flags: {
            type: :array,
            required: false,
            default: [],
            items: {
              type: :string
            }
          },
          dangerous_mode: {
            type: :hash,
            required: false,
            default: {},
            properties: {
              enabled: {
                type: :boolean,
                required: false,
                default: false
              },
              flags: {
                type: :array,
                required: false,
                default: [],
                items: {
                  type: :string
                }
              },
              auto_enable_in_devcontainer: {
                type: :boolean,
                required: false,
                default: true
              }
            }
          },
          models: {
            type: :array,
            required: false,
            default: [],
            items: {
              type: :string,
              pattern: /^[a-zA-Z0-9._-]+$/
            }
          },
          model_weights: {
            type: :hash,
            required: false,
            default: {},
            pattern_properties: {
              /^[a-zA-Z0-9._-]+$/ => {
                type: :integer,
                min: 1,
                max: 10
              }
            }
          },
          models_config: {
            type: :hash,
            required: false,
            default: {},
            pattern_properties: {
              /^[a-zA-Z0-9._-]+$/ => {
                type: :hash,
                properties: {
                  flags: {
                    type: :array,
                    required: false,
                    default: [],
                    items: {
                      type: :string
                    }
                  },
                  max_tokens: {
                    type: :integer,
                    required: false,
                    min: 1000,
                    max: 1_000_000
                  },
                  timeout: {
                    type: :integer,
                    required: false,
                    min: 30,
                    max: 3600
                  }
                }
              }
            }
          },
          auth: {
            type: :hash,
            required: false,
            default: {},
            properties: {
              api_key_env: {
                type: :string,
                required: false,
                pattern: /^[A-Z_][A-Z0-9_]*$/
              },
              api_key: {
                type: :string,
                required: false
              }
            }
          },
          endpoints: {
            type: :hash,
            required: false,
            default: {},
            properties: {
              default: {
                type: :string,
                required: false,
                format: :uri
              }
            }
          },
          features: {
            type: :hash,
            required: false,
            default: {},
            properties: {
              file_upload: {
                type: :boolean,
                required: false,
                default: false
              },
              code_generation: {
                type: :boolean,
                required: false,
                default: true
              },
              analysis: {
                type: :boolean,
                required: false,
                default: true
              },
              vision: {
                type: :boolean,
                required: false,
                default: false
              }
            }
          },
          monitoring: {
            type: :hash,
            required: false,
            default: {
              enabled: true,
              metrics_interval: 60
            },
            properties: {
              enabled: {
                type: :boolean,
                required: false,
                default: true
              },
              metrics_interval: {
                type: :integer,
                required: false,
                default: 60,
                min: 10,
                max: 3600
              }
            }
          }
        }
      }
    }
  },
  logging: {
    type: :hash,
    required: false,
    default: {},
    properties: {
      level: {
        type: :string,
        required: false,
        default: "info",
        enum: ["debug", "info", "warn", "error"]
      },
      json: {
        type: :boolean,
        required: false,
        default: false
      },
      max_size_mb: {
        type: :integer,
        required: false,
        default: 10,
        min: 1,
        max: 100
      },
      max_backups: {
        type: :integer,
        required: false,
        default: 5,
        min: 1,
        max: 20
      },
      max_age_days: {
        type: :integer,
        required: false,
        default: 14,
        min: 1,
        max: 365
      }
    }
  },
  auto_update: {
    type: :hash,
    required: false,
    default: {
      enabled: false,
      policy: "off",
      allow_prerelease: false,
      check_interval_seconds: 3600,
      supervisor: "none",
      max_consecutive_failures: 3
    },
    properties: {
      enabled: {
        type: :boolean,
        required: false,
        default: false
      },
      policy: {
        type: :string,
        required: false,
        default: "off",
        enum: ["off", "exact", "patch", "minor", "major"]
      },
      allow_prerelease: {
        type: :boolean,
        required: false,
        default: false
      },
      check_interval_seconds: {
        type: :integer,
        required: false,
        default: 3600,
        min: 300,
        max: 86400
      },
      supervisor: {
        type: :string,
        required: false,
        default: "none",
        enum: ["none", "supervisord", "s6", "runit"]
      },
      max_consecutive_failures: {
        type: :integer,
        required: false,
        default: 3,
        min: 1,
        max: 10
      }
    }
  }
}.freeze

Class Method Summary collapse

Class Method Details

.apply_defaults(config) ⇒ Object

Apply defaults to configuration



1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
# File 'lib/aidp/harness/config_schema.rb', line 1209

def self.apply_defaults(config)
  # Guard against non-hash config (e.g., when YAML parsing fails)
  return config unless config.is_a?(Hash)

  result = deep_dup(config)

  # Apply harness defaults
  if result.key?(:harness) || result.key?("harness")
    harness_section = result[:harness] || result["harness"]
    result[:harness] = apply_section_defaults(harness_section, SCHEMA[:harness])
  else
    result[:harness] = apply_section_defaults({}, SCHEMA[:harness])
  end

  # Apply provider defaults
  if result.key?(:providers) || result.key?("providers")
    providers_section = result[:providers] || result["providers"]
    result[:providers] = apply_providers_defaults(providers_section)
  else
    result[:providers] = {}
  end

  result
end

.apply_section_defaults(data, schema) ⇒ Object



1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
# File 'lib/aidp/harness/config_schema.rb', line 1571

def self.apply_section_defaults(data, schema)
  result = data.dup

  schema[:properties]&.each do |prop_name, prop_schema|
    if result.key?(prop_name) || result.key?(prop_name.to_s)
      prop_value = result[prop_name] || result[prop_name.to_s]
      if prop_schema[:type] == :hash && prop_schema[:properties]
        result[prop_name] = apply_section_defaults(prop_value, prop_schema)
      end
    elsif prop_schema[:default]
      result[prop_name] = prop_schema[:default]
    elsif prop_schema[:type] == :hash && prop_schema[:properties]
      result[prop_name] = apply_section_defaults({}, prop_schema)
    end
  end

  result
end

.cross_validate(config) ⇒ Object



1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
# File 'lib/aidp/harness/config_schema.rb', line 1523

def self.cross_validate(config)
  errors = []
  warnings = []

  # Validate that default_provider exists in providers
  harness_config = config[:harness] || config["harness"]
  providers_config = config[:providers] || config["providers"]

  if harness_config && providers_config
    default_provider = harness_config[:default_provider] || harness_config["default_provider"]
    if default_provider
      unless providers_config.key?(default_provider) || providers_config.key?(default_provider.to_sym)
        errors << "Default provider '#{default_provider}' not found in providers configuration"
      end
    end

    # Validate fallback providers exist
    fallback_providers = harness_config[:fallback_providers] || harness_config["fallback_providers"] || []
    fallback_providers.each do |provider|
      unless providers_config.key?(provider) || providers_config.key?(provider.to_sym)
        errors << "Fallback provider '#{provider}' not found in providers configuration"
      end
    end

    # Validate provider weights reference existing providers
    provider_weights = harness_config[:provider_weights] || harness_config["provider_weights"] || {}
    provider_weights.each do |provider, _weight|
      unless providers_config.key?(provider) || providers_config.key?(provider.to_sym)
        warnings << "Provider weight specified for non-existent provider '#{provider}'"
      end
    end
  end

  # Validate that models in model_weights exist in models array
  providers_config&.each do |provider_name, provider_config|
    models = provider_config[:models] || provider_config["models"] || []
    model_weights = provider_config[:model_weights] || provider_config["model_weights"] || {}

    model_weights.each do |model, _weight|
      unless models.include?(model)
        warnings << "Model weight specified for model '#{model}' not in models array for provider '#{provider_name}'"
      end
    end
  end

  [errors, warnings]
end

.generate_exampleObject

Generate example configuration



1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
# File 'lib/aidp/harness/config_schema.rb', line 1235

def self.generate_example
  {
    harness: {
      max_retries: 2,
      default_provider: "cursor",
      fallback_providers: ["cursor"],
      no_api_keys_required: false,
      provider_weights: {
        "cursor" => 3,
        "anthropic" => 2,
        "macos" => 1
      },
      circuit_breaker: {
        enabled: true,
        failure_threshold: 5,
        timeout: 300,
        half_open_max_calls: 3
      },
      retry: {
        enabled: true,
        max_attempts: 3,
        base_delay: 1.0,
        max_delay: 60.0,
        exponential_base: 2.0,
        jitter: true
      },
      rate_limit: {
        enabled: true,
        default_reset_time: 3600,
        burst_limit: 10,
        sustained_limit: 5
      },
      load_balancing: {
        enabled: true,
        strategy: "weighted_round_robin",
        health_check_interval: 30,
        unhealthy_threshold: 3
      },
      model_switching: {
        enabled: true,
        auto_switch_on_error: true,
        auto_switch_on_rate_limit: true,
        fallback_strategy: "sequential"
      },
      health_check: {
        enabled: true,
        interval: 60,
        timeout: 10,
        failure_threshold: 3,
        success_threshold: 2
      },
      metrics: {
        enabled: true,
        retention_days: 30,
        aggregation_interval: 300,
        export_interval: 3600
      },
      session: {
        enabled: true,
        timeout: 1800,
        sticky_sessions: true,
        session_affinity: "provider_model"
      }
    },
    providers: {
      cursor: {
        type: "subscription",
        priority: 1,
        default_flags: [],
        models: ["cursor-default", "cursor-fast", "cursor-precise"],
        model_weights: {
          "cursor-default" => 3,
          "cursor-fast" => 2,
          "cursor-precise" => 1
        },
        models_config: {
          "cursor-default" => {
            flags: [],
            timeout: 600
          },
          "cursor-fast" => {
            flags: ["--fast"],
            timeout: 300
          },
          "cursor-precise" => {
            flags: ["--precise"],
            timeout: 900
          }
        },
        features: {
          file_upload: true,
          code_generation: true,
          analysis: true
        },
        monitoring: {
          enabled: true,
          metrics_interval: 60
        }
      },
      anthropic: {
        type: "usage_based",
        priority: 2,
        max_tokens: 100_000,
        default_flags: ["--dangerously-skip-permissions"],
        models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"],
        model_weights: {
          "claude-3-5-sonnet-20241022" => 3,
          "claude-3-5-haiku-20241022" => 2
        },
        models_config: {
          "claude-3-5-sonnet-20241022" => {
            flags: ["--dangerously-skip-permissions"],
            max_tokens: 200_000,
            timeout: 300
          },
          "claude-3-5-haiku-20241022" => {
            flags: ["--dangerously-skip-permissions"],
            max_tokens: 200_000,
            timeout: 180
          }
        },
        auth: {
          api_key_env: "ANTHROPIC_API_KEY"
        },
        endpoints: {
          default: "https://api.anthropic.com/v1/messages"
        },
        features: {
          file_upload: true,
          code_generation: true,
          analysis: true,
          vision: true
        },
        monitoring: {
          enabled: true,
          metrics_interval: 60
        }
      },
      macos: {
        type: "passthrough",
        priority: 4,
        underlying_service: "cursor",
        models: ["cursor-chat"],
        features: {
          file_upload: false,
          code_generation: true,
          analysis: true,
          interactive: true
        }
      }
    }
  }
end

.validate(config) ⇒ Object

Validate configuration against schema



1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
# File 'lib/aidp/harness/config_schema.rb', line 1162

def self.validate(config)
  errors = []
  warnings = []

  # Validate top-level structure
  unless config.is_a?(Hash)
    errors << "Configuration must be a hash"
    return {valid: false, errors: errors, warnings: warnings}
  end

  # Validate harness section
  if config.key?(:harness) || config.key?("harness")
    harness_errors, harness_warnings = validate_section(
      config[:harness] || config["harness"],
      SCHEMA[:harness],
      "harness"
    )
    errors.concat(harness_errors)
    warnings.concat(harness_warnings)
  elsif SCHEMA[:harness][:required]
    errors << "harness: section is required"
  end

  # Validate providers section
  if config.key?(:providers) || config.key?("providers")
    providers_errors, providers_warnings = validate_section(
      config[:providers] || config["providers"],
      SCHEMA[:providers],
      "providers"
    )
    errors.concat(providers_errors)
    warnings.concat(providers_warnings)
  end

  # Cross-validation
  cross_validation_errors, cross_validation_warnings = cross_validate(config)
  errors.concat(cross_validation_errors)
  warnings.concat(cross_validation_warnings)

  {
    valid: errors.empty?,
    errors: errors,
    warnings: warnings
  }
end