Class: Aidp::Metadata::Validator

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/metadata/validator.rb

Overview

Validates tool metadata and detects issues

Performs validation checks on tool metadata including:

  • Required field presence

  • Field type validation

  • Duplicate ID detection

  • Dependency resolution

  • Version format validation

Examples:

Validating a collection of tools

validator = Validator.new(tools)
results = validator.validate_all
results.each { |r| puts "#{r[:file]}: #{r[:errors].join(", ")}" }

Defined Under Namespace

Classes: ValidationResult

Instance Method Summary collapse

Constructor Details

#initialize(tools = []) ⇒ Validator

Initialize validator with tool metadata collection

Parameters:

  • tools (Array<ToolMetadata>) (defaults to: [])

    Tools to validate



27
28
29
30
31
# File 'lib/aidp/metadata/validator.rb', line 27

def initialize(tools = [])
  @tools = tools
  @errors_by_id = {}
  @warnings_by_id = {}
end

Instance Method Details

#validate_allArray<ValidationResult>

Validate all tools

Returns:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/aidp/metadata/validator.rb', line 36

def validate_all
  Aidp.log_debug("metadata", "Validating all tools", count: @tools.size)

  results = @tools.map do |tool|
    validate_tool(tool)
  end

  # Cross-tool validations
  validate_duplicate_ids(results)
  validate_dependencies(results)

  Aidp.log_info(
    "metadata",
    "Validation complete",
    total: results.size,
    valid: results.count(&:valid),
    invalid: results.count { |r| !r.valid }
  )

  results
end

#validate_dependencies(results) ⇒ Object

Validate tool dependencies are satisfied

Parameters:



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/aidp/metadata/validator.rb', line 132

def validate_dependencies(results)
  available_ids = @tools.map(&:id).to_set

  @tools.each do |tool|
    next if tool.dependencies.empty?

    tool.dependencies.each do |dep_id|
      next if available_ids.include?(dep_id)

      result = results.find { |r| r.tool_id == tool.id && r.file_path == tool.source_path }
      next unless result

      result.errors << "Missing dependency: '#{dep_id}'"
      result.valid = false
    end
  end
end

#validate_deprecated_patterns(tool, warnings) ⇒ Object

Check for deprecated patterns in tool metadata

Parameters:

  • tool (ToolMetadata)

    Tool to check

  • warnings (Array<String>)

    Warnings array to append to



154
155
156
157
# File 'lib/aidp/metadata/validator.rb', line 154

def validate_deprecated_patterns(tool, warnings)
  # Check for legacy field usage (this would be expanded based on actual deprecations)
  # For now, this is a placeholder for future deprecation warnings
end

#validate_duplicate_ids(results) ⇒ Object

Validate for duplicate IDs across tools

Parameters:



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/aidp/metadata/validator.rb', line 108

def validate_duplicate_ids(results)
  ids = @tools.map(&:id)
  duplicates = ids.tally.select { |_, count| count > 1 }.keys

  return if duplicates.empty?

  duplicates.each do |dup_id|
    matching_tools = @tools.select { |t| t.id == dup_id }
    matching_tools.each do |tool|
      result = results.find { |r| r.tool_id == tool.id && r.file_path == tool.source_path }
      next unless result

      paths = matching_tools.map(&:source_path).join(", ")
      result.errors << "Duplicate ID '#{dup_id}' found in: #{paths}"
      result.valid = false
    end
  end

  Aidp.log_warn("metadata", "Duplicate IDs detected", duplicates: duplicates)
end

#validate_tool(tool) ⇒ ValidationResult

Validate a single tool

Parameters:

Returns:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/aidp/metadata/validator.rb', line 62

def validate_tool(tool)
  errors = []
  warnings = []

  # Tool metadata validates itself on initialization
  # Here we add additional cross-cutting validations

  # Check for empty arrays in key fields
  if tool.applies_to.empty? && tool.work_unit_types.empty?
    warnings << "No applies_to tags or work_unit_types specified (tool may not be discoverable)"
  end

  # Check for deprecated fields or patterns
  validate_deprecated_patterns(tool, warnings)

  # Check for experimental tools
  if tool.experimental
    warnings << "Tool is marked as experimental"
  end

  # Check content length
  if tool.content.length < 100
    warnings << "Tool content is very short (#{tool.content.length} characters)"
  end

  ValidationResult.new(
    tool_id: tool.id,
    file_path: tool.source_path,
    valid: errors.empty?,
    errors: errors,
    warnings: warnings
  )
rescue Aidp::Errors::ValidationError => e
  # Catch validation errors from tool initialization
  ValidationResult.new(
    tool_id: tool&.id || "unknown",
    file_path: tool&.source_path || "unknown",
    valid: false,
    errors: [e.message],
    warnings: []
  )
end

#write_error_log(results, log_path) ⇒ Object

Write validation errors to log file

Parameters:

  • results (Array<ValidationResult>)

    Validation results

  • log_path (String)

    Path to error log file



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/aidp/metadata/validator.rb', line 163

def write_error_log(results, log_path)
  Aidp.log_debug("metadata", "Writing error log", path: log_path)

  errors = results.reject(&:valid)
  return if errors.empty?

  File.open(log_path, "w") do |f|
    f.puts "# Metadata Validation Errors"
    f.puts "# Generated: #{Time.now.iso8601}"
    f.puts

    errors.each do |result|
      f.puts "## #{result.tool_id} (#{result.file_path})"
      f.puts
      result.errors.each { |err| f.puts "- ERROR: #{err}" }
      result.warnings.each { |warn| f.puts "- WARNING: #{warn}" }
      f.puts
    end
  end

  Aidp.log_info("metadata", "Wrote error log", path: log_path, error_count: errors.size)
end