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

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

Public API exports basic queue operations, priority support, and retry logic.
"""

from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry

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

### `queue/core.py`
```python
"""Core module: Job data model and basic FIFO queue operations."""

from dataclasses import dataclass, field
from typing import Any, Optional, List


@dataclass
class Job:
    """Represents a unit of work to be processed."""
    job_id: str
    payload: Any
    status: str = "pending"
    priority: int = 0
    attempts: int = 0
    max_retries: int = 3
    error: Optional[str] = None


class JobQueue:
    """Basic FIFO queue for managing Job instances."""
    
    def __init__(self) -> None:
        self._queue: List[Job] = []

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

    def remove(self) -> Optional[Job]:
        """Remove and return the first job in the queue."""
        return self._queue.pop(0) if self._queue else None

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

    def is_empty(self) -> bool:
        """Check if the queue contains no jobs."""
        return len(self._queue) == 0

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

### `queue/priority.py`
```python
"""Priority module: Priority-based job scheduling using a min-heap."""

import heapq
from typing import Optional, List
from .core import Job


class PriorityQueue:
    """Priority queue that processes lower priority values first.
    
    Uses a counter as a tie-breaker to maintain stable ordering 
    (FIFO for jobs with equal priority).
    """
    
    def __init__(self) -> None:
        self._heap: List[tuple] = []
        self._counter: int = 0

    def add(self, job: Job) -> None:
        """Add a job to the priority queue."""
        # Tuple structure: (priority, insertion_order, job)
        heapq.heappush(self._heap, (job.priority, self._counter, job))
        self._counter += 1

    def remove(self) -> Optional[Job]:
        """Remove and return the job with the highest priority (lowest value)."""
        if not self._heap:
            return None
        _, _, job = heapq.heappop(self._heap)
        return job

    def peek(self) -> Optional[Job]:
        """Return the highest priority job without removing it."""
        if not self._heap:
            return None
        return self._heap[0][2]

    def is_empty(self) -> bool:
        """Check if the priority queue is empty."""
        return len(self._heap) == 0

    def size(self) -> int:
        """Return the number of jobs in the priority queue."""
        return len(self._heap)
```

### `queue/retry.py`
```python
"""Retry module: Exponential backoff logic for failed job processing."""

import time
import random
from typing import Callable, Any, Optional
from .core import Job


def process_with_retry(
    job: Job,
    processor: Callable[[Job], Any],
    max_retries: Optional[int] = None,
    base_delay: float = 1.0,
    max_delay: float = 60.0,
    jitter: bool = True
) -> Any:
    """Process a job with automatic retry and exponential backoff.
    
    Args:
        job: The job instance to process.
        processor: Callable that executes the job's payload.
        max_retries: Maximum number of retry attempts. Falls back to job.max_retries if None.
        base_delay: Base delay in seconds for exponential backoff.
        max_delay: Maximum delay cap in seconds.
        jitter: Whether to add random jitter to prevent thundering herd.
        
    Returns:
        The result of the processor function.
        
    Raises:
        The last exception if all retries are exhausted.
    """
    retries = max_retries if max_retries is not None else job.max_retries
    last_exception: Optional[Exception] = None

    for attempt in range(retries + 1):
        try:
            job.attempts = attempt + 1
            job.status = "processing"
            result = processor(job)
            job.status = "completed"
            return result
        except Exception as e:
            last_exception = e
            job.error = str(e)
            job.status = "failed"

            if attempt < retries:
                delay = min(base_delay * (2 ** attempt), max_delay)
                if jitter:
                    delay *= random.uniform(0.5, 1.5)
                time.sleep(delay)

    job.status = "exhausted"
    raise last_exception
```

### Key Design Decisions:
1. **Single Responsibility**: `core.py` handles data modeling & FIFO logic, `priority.py` handles heap-based scheduling, `retry.py` handles failure recovery.
2. **No Circular Imports**: `__init__.py` only imports from leaf modules; leaf modules only import from `core.py`.
3. **Stable Priority Ordering**: The counter in `PriorityQueue` ensures FIFO behavior when priorities are equal.
4. **Configurable Backoff**: `process_with_retry` supports max delay caps and jitter to prevent retry storms.
5. **Job State Tracking**: Status and attempt counts are updated atomically during processing for easy monitoring/debugging.