validateInput method

  1. @override
ValidationResult validateInput(
  1. Map<String, dynamic> input
)
override

Validate input before execution and permission checks.

Implementation

@override
ValidationResult validateInput(Map<String, dynamic> input) {
  final to = input['to'] as String?;
  if (to == null || to.trim().isEmpty) {
    return const ValidationResult.invalid('to must not be empty');
  }

  // Parse address for validation
  final addr = parseAddress(to);

  // Check for empty address targets
  if ((addr.scheme == 'bridge' || addr.scheme == 'uds') &&
      addr.target.trim().isEmpty) {
    return const ValidationResult.invalid('address target must not be empty');
  }

  // Reject @ notation — there is only one team per session
  if (to.contains('@')) {
    return const ValidationResult.invalid(
      'to must be a bare teammate name or "*" -- there is only one '
      'team per session',
    );
  }

  final message = input['message'];

  // Plain text message validation
  if (message is String) {
    // Bridge cross-session: structured messages not allowed
    if (addr.scheme == 'bridge') {
      // Structured messages cannot be sent cross-session
      return const ValidationResult.valid();
    }
    // UDS cross-session: summary not required
    if (addr.scheme == 'uds') {
      return const ValidationResult.valid();
    }
    // Local plain text: summary is required
    final summary = input['summary'] as String?;
    if (summary == null || summary.trim().isEmpty) {
      return const ValidationResult.invalid(
        'summary is required when message is a string',
      );
    }
    return const ValidationResult.valid();
  }

  // Structured message validation
  if (message is Map) {
    // Structured messages cannot be broadcast
    if (to == '*') {
      return const ValidationResult.invalid(
        'structured messages cannot be broadcast (to: "*")',
      );
    }

    // Structured messages cannot be sent cross-session
    if (addr.scheme != 'other') {
      return const ValidationResult.invalid(
        'structured messages cannot be sent cross-session -- '
        'only plain text',
      );
    }

    final type = message['type'] as String?;

    // shutdown_response must be sent to team-lead
    if (type == 'shutdown_response' && to != teamLeadName) {
      return ValidationResult.invalid(
        'shutdown_response must be sent to "$teamLeadName"',
      );
    }

    // Rejecting a shutdown requires a reason
    if (type == 'shutdown_response') {
      final approve = message['approve'] as bool? ?? false;
      if (!approve) {
        final reason = message['reason'] as String?;
        if (reason == null || reason.trim().isEmpty) {
          return const ValidationResult.invalid(
            'reason is required when rejecting a shutdown request',
          );
        }
      }
    }

    return const ValidationResult.valid();
  }

  return const ValidationResult.invalid(
    'message must be a string or structured message object',
  );
}