Here’s the complete refactored package structure. Each file follows the single-responsibility principle, avoids circular imports, and exposes a clean public API.

### `queue/__init__.py`
```python
"""
Job Queue Package
Public API: Job, JobQueue
"""
from .core import Job, JobQueue

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

### `queue/core.py`
```python
"""
Core components: Job model and main queue orchestrator.
"""
from dataclasses import dataclass, field
from typing import Callable, Any, Optional
from enum import Enum
import uuid

from .priority import PriorityQueue
from .retry import process_with_retry

class JobStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class Job:
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    func: Callable[..., Any] = None
    args: tuple = ()
    kwargs: dict = field(default_factory=dict)
    priority: int = 0
    status: JobStatus = JobStatus.PENDING
    result: Any = None
    error: str = None
    retry_count: int = 0
    max_retries: int = 3

class JobQueue:
    """Orchestrates job submission, priority ordering, and execution."""

    def __init__(self) -> None:
        self._queue = PriorityQueue()
        self._jobs: dict[str, Job] = {}

    def add_job(self, func: Callable[..., Any], args: tuple = (), kwargs: dict = None, priority: int = 0) -> Job:
        """Register a new job and push it to the priority queue."""
        kwargs = kwargs or {}
        job = Job(func=func, args=args, kwargs=kwargs, priority=priority)
        self._jobs[job.id] = job
        self._queue.push(job)
        return job

    def process_next(self) -> Optional[Job]:
        """Dequeue and execute the highest-priority job with retry logic."""
        if self._queue.is_empty():
            return None

        job = self._queue.pop()
        try:
            process_with_retry(job)
        except Exception:
            # Job status is already set to FAILED by process_with_retry
            pass

        return job

    def get_job(self, job_id: str) -> Optional[Job]:
        """Retrieve a job by its unique ID."""
        return self._jobs.get(job_id)
```

### `queue/priority.py`
```python
"""
Priority queue implementation using a min-heap.
"""
import heapq
from typing import List, TYPE_CHECKING

if TYPE_CHECKING:
    from .core import Job

class PriorityQueue:
    """Thread-unsafe priority queue that orders jobs by priority (lower = higher priority)."""

    def __init__(self) -> None:
        self._heap: List = []
        self._counter = 0  # Tie-breaker to maintain FIFO order for equal priorities

    def push(self, job: "Job") -> None:
        entry = (job.priority, self._counter, job)
        self._counter += 1
        heapq.heappush(self._heap, entry)

    def pop(self) -> "Job":
        if self._heap:
            _, _, job = heapq.heappop(self._heap)
            return job
        raise IndexError("pop from empty priority queue")

    def __len__(self) -> int:
        return len(self._heap)

    def is_empty(self) -> bool:
        return len(self._heap) == 0
```

### `queue/retry.py`
```python
"""
Retry logic with exponential backoff.
"""
import time
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .core import Job

def process_with_retry(job: "Job") -> None:
    """
    Execute a job with exponential backoff on failure.
    Updates job status, result/error, and retry count.
    """
    # Local import prevents circular dependency at module initialization time
    from .core import JobStatus

    for attempt in range(job.max_retries + 1):
        try:
            job.status = JobStatus.RUNNING
            result = job.func(*job.args, **job.kwargs)
            job.status = JobStatus.COMPLETED
            job.result = result
            return
        except Exception as e:
            job.retry_count += 1
            if attempt < job.max_retries:
                # Exponential backoff: 1s, 2s, 4s, ...
                time.sleep(2 ** attempt)
            else:
                job.status = JobStatus.FAILED
                job.error = str(e)
                raise
```

### Key Design Decisions
1. **Circular Import Avoidance**: `priority.py` and `retry.py` use `TYPE_CHECKING` for type hints, so they don't import `core` at runtime. `retry.py` imports `JobStatus` locally inside the function to safely break the initialization cycle.
2. **Single Responsibility**: 
   - `core.py` handles the domain model (`Job`) and orchestration (`JobQueue`).
   - `priority.py` strictly manages heap-based ordering.
   - `retry.py` encapsulates execution + backoff logic.
   - `__init__.py` curates the public API.
3. **Clean Public API**: Users only interact with `Job` and `JobQueue`. Internal components (`PriorityQueue`, `process_with_retry`) remain encapsulated.
4. **Stable Ordering**: `PriorityQueue` uses a monotonic counter as a tie-breaker so jobs with equal priority are processed in FIFO order.

### Usage Example
```python
from queue import JobQueue

def task(x, y):
    if x < 0:
        raise ValueError("Negative input")
    return x + y

q = JobQueue()
q.add_job(task, args=(1, 2), priority=1)
q.add_job(task, args=(-1, 2), priority=2)  # Will retry 3 times then fail

result_job = q.process_next()
print(result_job.result)  # 3
```