This refactoring separates concerns into distinct modules, making the code easier to test, maintain, and scale.

Here is the content for each file in the `queue/` directory.

---

### `queue/core.py` (Job definition and basic queue structure)

This module handles the fundamental data structures: the Job itself and the primary Queue container.

```python
# queue/core.py

from dataclasses import dataclass
from typing import Callable, Any, Optional

# --- Job Definition ---

@dataclass(order=True)
class Job:
    """
    Represents a single unit of work.
    The 'priority' attribute is used for ordering in the PriorityQueue.
    """
    priority: int
    job_id: str
    payload: Any
    # Custom comparison is needed for heapq when priorities are equal
    # We use job_id as a tie-breaker to ensure consistent ordering
    _tie_breaker: str = "" 

    def __post_init__(self):
        # Ensure the tie-breaker is unique for stable sorting
        if not self._tie_breaker:
            import uuid
            self._tie_breaker = str(uuid.uuid4())

    def __lt__(self, other):
        # Custom comparison logic for the heap
        return (self.priority, self._tie_breaker) < (other.priority, other._tie_breaker)

# --- Job Queue Implementation ---

class JobQueue:
    """
    Manages the collection of jobs, utilizing a PriorityQueue internally.
    """
    def __init__(self):
        # The actual queue implementation will be imported from priority.py
        from .priority import PriorityQueue
        self._queue = PriorityQueue()

    def enqueue(self, job: Job):
        """Adds a job to the queue."""
        self._queue.push(job)
        print(f"Job {job.job_id} enqueued with priority {job.priority}.")

    def dequeue(self) -> Optional[Job]:
        """Removes and returns the highest priority job."""
        return self._queue.pop()

    def is_empty(self) -> bool:
        """Checks if the queue is empty."""
        return self._queue.is_empty()
```

### `queue/priority.py` (Priority Queue implementation)

This module encapsulates the logic for maintaining order, typically using Python's `heapq`.

```python
# queue/priority.py

import heapq
from typing import List, Any
from .core import Job # Import the Job dataclass

class PriorityQueue:
    """
    A min-heap implementation used to store and retrieve the highest priority Job.
    (Note: Since Job is ordered by priority, standard heap functions work.)
    """
    def __init__(self):
        # The heap stores the Job objects
        self._heap: List[Job] = []

    def push(self, job: Job):
        """Adds a job to the heap."""
        heapq.heappush(self._heap, job)

    def pop(self) -> Job:
        """Removes and returns the job with the highest priority (smallest priority number)."""
        if self.is_empty():
            raise IndexError("pop from empty priority queue")
        return heapq.heappop(self._heap)

    def is_empty(self) -> bool:
        """Checks if the queue is empty."""
        return not bool(self._heap)
```

### `queue/retry.py` (Retry and execution logic)

This module handles the external concerns of job execution, failure tracking, and backoff timing.

```python
# queue/retry.py

import time
from typing import Callable
from .core import Job, JobQueue

# Base delay for exponential backoff
BASE_DELAY = 2  # seconds

def process_with_retry(
    job: Job, 
    job_queue: JobQueue, 
    execution_function: Callable[[Job], bool], 
    max_retries: int = 3
) -> bool:
    """
    Executes a job with exponential backoff retry logic.

    Args:
        job: The job to execute.
        job_queue: The queue used to re-enqueue the job upon failure.
        execution_function: A function that performs the actual work. 
                             It must return True on success, False on failure.
        max_retries: The maximum number of attempts before failing permanently.

    Returns:
        True if the job succeeded, False otherwise.
    """
    current_attempt = 0
    while current_attempt < max_retries:
        try:
            print(f"\n--- Attempt {current_attempt + 1} for Job {job.job_id} ---")
            
            # Execute the job
            success = execution_function(job)

            if success:
                print(f"Job {job.job_id} completed successfully.")
                return True
            else:
                # Execution failed (e.g., API returned 500)
                raise Exception("Execution failed gracefully.")

        except Exception as e:
            current_attempt += 1
            if current_attempt >= max_retries:
                print(f"Job {job.job_id} permanently failed after {max_retries} attempts. Error: {e}")
                return False
            
            # Calculate exponential backoff delay: BASE_DELAY * (2^attempt)
            delay = BASE_DELAY * (2 ** (current_attempt - 1))
            print(f"Job {job.job_id} failed. Retrying in {delay:.2f} seconds...")
            time.sleep(delay)
            
            # Re-enqueue the job back into the queue for the next attempt
            job_queue.enqueue(job)
            return True # Return True here to signify the retry mechanism took over

    return False
```

### `queue/__init__.py` (Public API)

This file defines the public interface, allowing users to import the necessary components cleanly.

```python
# queue/__init__.py

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

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

# Example usage demonstration (optional, for testing the structure)
if __name__ == '__main__':
    print("--- Testing Queue Package Structure ---")
    
    # Initialize core components
    queue_manager = JobQueue()

    # Define a dummy task function
    def dummy_task(job: Job) -> bool:
        """Simulates a task that fails twice, then succeeds."""
        if not hasattr(dummy_task, 'failure_count'):
            dummy_task.failure_count = 0
        
        dummy_task.failure_count += 1
        
        print(f"  [Task Runner] Running job {job.job_id}. Attempt count: {dummy_task.failure_count}")
        
        if dummy_task.failure_count < 3:
            return False # Simulate failure
        else:
            return True # Simulate success

    # Create jobs
    job1 = Job(priority=1, job_id="A_High", payload={"data": 1})
    job2 = Job(priority=5, job_id="B_Low", payload={"data": 2})
    
    # Enqueue jobs
    queue_manager.enqueue(job1)
    queue_manager.enqueue(job2)

    # Dequeue and process the highest priority job (A_High)
    job_to_process = queue_manager.dequeue()
    
    if job_to_process:
        print("\nStarting processing loop...")
        # Process with retry logic
        success = process_with_retry(
            job=job_to_process,
            job_queue=queue_manager,
            execution_function=dummy_task,
            max_retries=5
        )
        print(f"\nFinal status for Job {job_to_process.job_id}: {'SUCCESS' if success else 'FAILURE'}")
    else:
        print("Queue was empty.")
```

### Summary of Responsibilities

| File | Responsibility | Dependencies | Public Exports |
| :--- | :--- | :--- | :--- |
| `core.py` | Defines the `Job` data structure and the basic `JobQueue` container. | `priority.py` | `Job`, `JobQueue` |
| `priority.py` | Implements the heap/priority logic (`PriorityQueue`). | `core.py` | `PriorityQueue` |
| `retry.py` | Implements external business logic: execution, failure handling, and backoff timing. | `core.py` (Job, JobQueue) | `process_with_retry` |
| `__init__.py` | Acts as the package entry point, defining the clean API. | All others | `Job`, `JobQueue`, `process_with_retry` |