```ts
/**
 * Parses a span text into total inches.
 */
export function parseSpan(text: string): number {
  if (typeof text !== 'string' || !text.trim()) {
    throw new Error('Invalid span format');
  }

  const parts = text.split(/\s+/).filter(Boolean);
  const units = ['yd', 'ft', 'in'];
  let totalInches = 0;

  for (const part of parts) {
    const match = part.match(/^(\d+)([a-z]{2})$/i);
    if (!match || !units.includes(match[2])) {
      throw new Error('Invalid span format');
    }

    const value = parseInt(match[1], 10);
    if (isNaN(value) || value < 0) {
      throw new Error('Invalid span format');
    }

    switch (match[2].toLowerCase()) {
      case 'yd':
        totalInches += value * 36;
        break;
      case 'ft':
        totalInches += value * 12;
        break;
      case 'in':
        totalInches += value;
        break;
    }
  }

  return totalInches;
}

/**
 * Renders a non-negative integer of total inches in canonical form.
 */
export function formatSpan(inches: number): string {
  if (typeof inches !== 'number' || !Number.isInteger(inches) || inches < 0) {
    throw new Error('Invalid inches value');
  }

  let yards = Math.floor(inches / 36);
  inches %= 36;
  let feet = Math.floor(inches / 12);
  inches %= 12;

  const parts: string[] = [];
  if (yards > 0) {
    parts.push(`${yards}yd`);
  }
  if (feet > 0) {
    parts.push(`${feet}ft`);
  }
  if (inches > 0) {
    parts.push(`${inches}in`);
  }

  return parts.join(' ') || '0in';
}

/**
 * Adds two span texts and returns their sum in canonical form.
 */
export function addSpans(first: string, second: string): string {
  const firstInches = parseSpan(first);
  const secondInches = parseSpan(second);
  return formatSpan(firstInches + secondInches);
}
```