```ts
export function sieveReport(items: Array<Record<string, unknown>>, rules: Array<Record<string, unknown>>): string[] {
  const validOps = new Set(['eq', 'ne', 'gt', 'lt', 'present', 'absent']);
  const ruleNames = new Set<string>();

  for (const rule of rules) {
    if (typeof rule.name !== 'string' || rule.name.trim() === '') {
      throw new Error('Rule name must be a non-empty string');
    }
    if (ruleNames.has(rule.name)) {
      throw new Error(`Duplicate rule name: ${rule.name}`);
    }
    ruleNames.add(rule.name);

    if (!validOps.has(rule.op as string)) {
      throw new Error(`Invalid op: ${rule.op}`);
    }

    if ((rule.op === 'eq' || rule.op === 'ne') && typeof rule.value === 'undefined') {
      throw new Error('Value must be provided for "eq" and "ne" ops');
    }
  }

  return items.map(item => {
    for (const rule of rules) {
      const { name, field, op, value } = rule as { name: string; field: string; op: string; value?: unknown };

      if (!item.hasOwnProperty(field)) {
        if (op === 'absent') continue;
        return name;
      }

      const itemValue = item[field];

      switch (op) {
        case 'eq':
          if (itemValue !== value) return name;
          break;
        case 'ne':
          if (itemValue === value) return name;
          break;
        case 'gt':
          if (typeof itemValue !== 'number' || typeof value !== 'number' || itemValue <= value) return name;
          break;
        case 'lt':
          if (typeof itemValue !== 'number' || typeof value !== 'number' || itemValue >= value) return name;
          break;
        case 'present':
          continue;
        case 'absent':
          return name;
      }
    }

    return 'pass';
  });
}
```