```ts
/**
 * Starts a patrol bot at the given start position and applies a list of moves,
 * counting wall bumps and distinct cells visited (including the start).
 * Rejects invalid width, start, move, or moves arguments with an error.
 *
 * @param width - The width of the corridor, must be a positive integer.
 * @param start - The starting position of the bot, must be an integer inside the corridor.
 * @param moves - A list of non-zero integers representing moves to apply.
 * @returns An object containing the final position, wall bumps, and visited cells count.
 */
export function runPatrol(width: number, start: number, moves: number[]): Record<string, number> {
  if (!Number.isInteger(width) || width <= 0) {
    throw new Error('Invalid corridor width');
  }
  if (!Number.isInteger(start) || start < 0 || start >= width) {
    throw new Error('Invalid start position');
  }
  if (!Array.isArray(moves)) {
    throw new Error('Moves must be a list of integers');
  }

  let position = start;
  let bumps = 0;
  const visited: Set<number> = new Set([position]);

  for (const move of moves) {
    if (!Number.isInteger(move) || move === 0) {
      throw new Error('Invalid move');
    }
    [position, bumps] = clampMove(position, move, width);
    visited.add(position);
  }

  return { position, bumps, visited: visited.size };
}

/**
 * Applies a single move to the bot's current position, clamping to the corridor walls.
 *
 * @param position - The current position of the bot.
 * @param delta - The move to apply (positive or negative).
 * @param width - The width of the corridor.
 * @returns A tuple containing the new position and a boolean indicating if a wall was hit.
 */
export function clampMove(position: number, delta: number, width: number): [number, boolean] {
  const newPosition = position + delta;
  return [Math.max(0, Math.min(newPosition, width - 1)), newPosition !== position];
}
```