Here's the refactored package structure with clean separation of concerns, proper relative imports, and a focused public API.

### 📁 `queue/__init__.py`
```python
"""Job Queue Package.

Public API exports only the core queue and job components.
Priority and retry functionality is accessible via submodules.
"""

from .core import Job, JobQueue

__all__ = ["Job", "JobQueue"]
```

### 📁 `queue/core.py`
```python
"""Core job data model and basic FIFO queue operations."""

from collections import deque
from dataclasses import dataclass, field
from threading import Lock
from typing import Any, Optional
import time


@dataclass
class Job:
    """Represents a unit of work to be processed."""
    id: str
    payload: Any
    status: str = "pending"
    created_at: float = field(default_factory=time.time)
    retries: int = 0


class JobQueue:
    """Thread-safe FIFO job queue."""
    
    def __init__(self) -> None:
        self._queue: deque[Job] = deque()
        self._lock: Lock = Lock()

    def add(self, job: Job) -> None:
        """Add a job to the end of the queue."""
        with self._lock:
            self._queue.append(job)

    def pop(self) -> Optional[Job]:
        """Remove and return the next job from the front of the queue."""
        with self._lock:
            return self._queue.popleft() if self._queue else None

    def peek(self) -> Optional[Job]:
        """Return the next job without removing it."""
        with self._lock:
            return self._queue[0] if self._queue else None

    def __len__(self) -> int:
        """Return the number of jobs in the queue."""
        with self._lock:
            return len(self._queue)
```

### 📁 `queue/priority.py`
```python
"""Priority queue implementation for ordered job execution."""

from heapq import heappop, heappush
from threading import Lock
from typing import Any, Optional

from .core import Job


class PriorityQueue:
    """Thread-safe priority queue. Lower integer values = higher priority."""
    
    def __init__(self) -> None:
        self._queue: list[tuple[int, int, Job]] = []
        self._counter: int = 0  # Tie-breaker for equal priorities
        self._lock: Lock = Lock()

    def push(self, job: Job, priority: int = 0) -> None:
        """Add a job with a given priority level."""
        with self._lock:
            entry = (priority, self._counter, job)
            self._counter += 1
            heappush(self._queue, entry)

    def pop(self) -> Optional[Job]:
        """Remove and return the highest priority job."""
        with self._lock:
            if self._queue:
                _, _, job = heappop(self._queue)
                return job
            return None

    def __len__(self) -> int:
        """Return the number of jobs in the priority queue."""
        with self._lock:
            return len(self._queue)
```

### 📁 `queue/retry.py`
```python
"""Retry logic with exponential backoff for job processing."""

import logging
import time
from typing import Any, Callable, Optional

from .core import Job

logger = logging.getLogger(__name__)


def process_with_retry(
    job: Job,
    processor: Callable[[Job], Any],
    max_retries: int = 3,
    base_delay: float = 1.0,
    backoff_factor: float = 2.0
) -> Optional[Any]:
    """Execute a job with automatic retry and exponential backoff.
    
    Args:
        job: The job to process.
        processor: Callable that accepts a Job and returns a result.
        max_retries: Maximum number of retry attempts.
        base_delay: Initial delay in seconds between retries.
        backoff_factor: Multiplier applied to delay after each retry.
        
    Returns:
        The processor's result on success, or None after exhausting retries.
    """
    last_exception: Optional[Exception] = None
    
    for attempt in range(max_retries + 1):
        try:
            result = processor(job)
            job.status = "completed"
            return result
        except Exception as e:
            last_exception = e
            job.retries = attempt + 1
            job.status = "failed"
            
            if attempt < max_retries:
                delay = base_delay * (backoff_factor ** attempt)
                logger.warning(
                    f"Attempt {attempt + 1} failed for job {job.id}: {e}. "
                    f"Retrying in {delay}s..."
                )
                time.sleep(delay)
                
    job.status = "dead_letter"
    logger.error(f"Job {job.id} exhausted all retries. Last error: {last_exception}")
    return None
```

### 🔑 Key Design Decisions
1. **Single Responsibility**: Each module handles exactly one concern (`core` → data & FIFO, `priority` → ordering, `retry` → resilience).
2. **Thread Safety**: Both queue implementations use `threading.Lock` to prevent race conditions during concurrent `pop`/`push` operations.
3. **Relative Imports**: All cross-module imports use `.` notation (e.g., `from .core import Job`), ensuring the package works correctly when installed or run as a module.
4. **Clean Public API**: `__init__.py` explicitly exposes only `Job` and `JobQueue`. Advanced features (`PriorityQueue`, `process_with_retry`) remain accessible via `queue.priority` and `queue.retry` without polluting the top-level namespace.
5. **Modern Python**: Uses `dataclasses`, type hints, `collections.deque` for O(1) FIFO operations, and `heapq` for O(log n) priority ordering. Compatible with Python 3.9+.