<reasoning_content>
*   Input: A request to refactor a job queue into three specific classes (`Job`, `RetryPolicy`, `JobQueue`) in a file named `solution.py`.
    *   Constraint 1: `Job` must be a `@dataclass` with `id`, `data`, `priority`, and `retries`.
    *   Constraint 2: `RetryPolicy` handles retries with backoff (no actual sleeping), takes a function and data, returns `(success, attempts_made)`.
    *   Constraint 3: `JobQueue` manages jobs using `RetryPolicy`, includes `add_job`, `process_job` (must use `RetryPolicy`), and `get_next_job` (highest priority first, FIFO within priority).
    *   Constraint 4: Use only standard library.
    *   Constraint 5: No explanations, only Python code.

    *   *Job*:
        ```python
        from dataclasses import dataclass
        from typing import Any, Callable

        @dataclass
        class Job:
            id: str
            data: dict
            priority: int = 0
            retries: int = 0
        ```

    *   *RetryPolicy*:
        ```python
        class RetryPolicy:
            def __init__(self, max_attempts: int = 4):
                self.max_attempts = max_attempts

            def run(self, fn: Callable[[dict], Any], data: dict) -> tuple[bool, int]:
                attempts_made = 0
                while attempts_made < self.max_attempts:
                    try:
                        fn(data)
                        return True, attempts_made + 1
                    except Exception:
                        attempts_made += 1
                return False, attempts_made
        ```

    *   *JobQueue*:
        Need to store jobs such that they can be retrieved by priority (descending) and then FIFO. A list sorted or a priority queue could work. Since it's standard library, `heapq` is usually best for priority, but `heapq` is min-heap. For max-priority, we can negate the priority.
        Wait, `get_next_job` says "return the highest-priority job's (id, data); FIFO order within the same priority".
        Actually, keeping a list and sorting or finding max works well for small numbers of items, but a Priority Queue is standard.
        Let's use a list and `sort` or simply find the max to satisfy the FIFO/priority requirement simply. Or use `heapq` with a tuple `(-priority, counter, job)`.

        Let's look at `process_job`: "must USE RetryPolicy".
        ```python
        class JobQueue:
            def __init__(self):
                self.jobs = [] # List of Job objects
                self.retry_policy = RetryPolicy()

            def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
                new_job = Job(id=job_id, data=data, priority=priority)
                self.jobs.append(new_job)

            def process_job(self, job_id: str, processor: Callable[[dict], Any]) -> bool:
                # Find the job by ID
                job = next((j for j in self.jobs if j.id == job_id), None)
                if not job:
                    return False
                
                success, _ = self.retry_policy.run(processor, job.data)
                return success

            def get_next_job(self) -> tuple[str, dict] | None:
                if not self.jobs:
                    return None
                
                # Sort by priority DESC, then by original insertion order (FIFO)
                # Since list is appended, lower index = older = FIFO.
                # We want highest priority (max).
                # Sort key: (-priority, list_index)
                # To keep it simple, sort by priority descending.
                # Because list.append maintains order, stable sort works.
                self.jobs.sort(key=lambda x: x.priority, reverse=True)
                # Wait, stable sort keeps relative order of items with same priority.
                # Let's just find the max priority job and return the first one found.
                
                # Better implementation:
                # Find max priority
                max_priority = max(j.priority for j in self.jobs)
                # Find first job with that priority
                for j in self.jobs:
                    if j.priority == max_priority:
                        # Remove it or just return? The prompt doesn't say to remove.
                        # Usually queues consume. Let's assume it stays unless specified?
                        # "Return the highest-priority job". Usually implies removal or Peek.
                        # "Queue" implies removal.
                        self.jobs.remove(j)
                        return j.id, j.data
                return None
        ```

    *   *Refining `JobQueue`*:
        The prompt asks to "Return the highest-priority job's (id, data)". In a queue context, usually you remove it.
        Let's use `sort` with `reverse=True`. Because `sort` is stable in Python, jobs with the same priority will remain in their original relative order (FIFO).

        ```python
        def get_next_job(self) -> tuple[str, dict] | None:
            if not self.jobs:
                return None
            # Stable sort maintains order of equal keys.
            # Higher priority comes first.
            self.jobs.sort(key=lambda x: x.priority, reverse=True)
            job = self.jobs.pop(0)
            return job.id, job.data
        ```

    *   *RetryPolicy Details*:
        "calls `fn(data)`, retries on any exception until success or `max_attempts` reached, returns `(success, attempts_made)`."
        "Do NOT actually sleep."

    *   *Imports*: `dataclass`, `Callable`, `Any`, `Optional`.

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

    @dataclass
    class Job:
        id: str
        data: dict
        priority: int = 0
        retries: int = 0

    class RetryPolicy:
        def __init__(self, max_attempts: int = 4):
            self.max_attempts = max_attempts

        def run(self, fn: Callable[[dict], Any], data: dict) -> tuple[bool, int]:
            attempts_made = 0
            while attempts_made < self.max_attempts:
                try:
                    fn(data)
                    return True, attempts_made + 1
                except Exception:
                    attempts_made += 1
            return False, attempts_made

    class JobQueue:
        def __init__(self):
            self.jobs: list[Job] = []
            self.retry_policy = RetryPolicy()

        def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
            self.jobs.append(Job(id=job_id, data=data, priority=priority)))

        def process_job(self, job_id: str, processor: Callable[[dict], Any]) -> bool:
            job = next((j for j in self.jobs if j.id == job_id), None)
            if job is None:
                return False
            
            success, _ = self.retry_policy.run(processor, job.data)
            return success

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self.jobs:
                return None
            
            # Sort by priority descending. Python's sort is stable,
            # preserving FIFO order for identical priorities.
            self.jobs.sort(key=lambda x: x.priority, reverse=True)
            job = self.jobs.pop(0)
            return job.id, job.data
    ```

    One detail: `process_job` - does it remove the job? The prompt doesn't specify. Usually, processing a job in a queue implies completion/removal, but `process_job` takes an ID. If I process it, should it stay in the queue? Most queue implementations remove the item after processing. However, `get_next_job` specifically returns the next one. 
    I will keep `process_job` as a method that takes an ID and doesn't necessarily remove it unless it's meant to be destructive. Actually, looking at `get_next_job`, that is the primary way to retrieve jobs. `process_job` might be a way to execute a specific one.

    Actually, looking at the phrasing: "orchestrates Jobs using RetryPolicy".
    If `process_job` succeeds, should I remove it?
    Prompt: `process_job(self, job_id: str, processor: Callable) -> bool`.
    I'll stick to the logic that `process_job` just executes the logic.

    One more check on `RetryPolicy.run`: "calls `fn(data)`, retries on any exception until success or `max_attempts` reached".
    If `max_attempts` is 1, it tries once. If it fails, it returns `(False, 1)`. Correct.

    One more check on `JobQueue.get_next_job`: "return the highest-priority job's (id, data); FIFO order within the same priority."
    Using `sort(key=lambda x: x.priority, reverse=True)` followed by `pop(0)` is the cleanest way to satisfy "highest-priority" and "FIFO within same priority" because Python's sort is stable.

    Wait, I should make sure I don't use `Optional` if I can use `| None`. The prompt uses `tuple[str, dict] | None`. This is Python 3.10+ syntax. I will stick to that.

    Check `RetryPolicy.run` signature: `run(self, fn: Callable, data) -> tuple[bool, int]`.
    My signature: `run(self, fn: Callable[[dict], Any], data: dict) -> tuple[bool, int]`. I'll simplify the type hint to match the prompt more closely.

    ```python
    from dataclasses import dataclass
    from typing import Callable, Any

    @dataclass
    class Job:
        id: str
        data: dict
        priority: int = 0
        retries: int = 0

    class RetryPolicy:
        def __init__(self, max_attempts: int = 4):
            self.max_attempts = max_attempts

        def run(self, fn: Callable, data: dict) -> tuple[bool, int]:
            attempts_made = 0
            while attempts_made < self.max_attempts:
                try:
                    fn(data)
                    return True, attempts_made + 1
                except Exception:
                    attempts_made += 1
            return False, attempts_made

    class JobQueue:
        def __init__(self):
            self.jobs: list[Job] = []
            self.retry_policy = RetryPolicy()

        def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
            self.jobs.append(Job(id=job_id, data=data, priority=priority)))

        def process_job(self, job_id: str, processor: Callable) -> bool:
            job = next((j for j in self.jobs if j.id == job_id), None)
            if job is None:
                return False
            
            success, _ = self.retry_policy.run(processor, job.data)
            return success

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self.jobs:
                return None
            
            # Python's sort is stable, so items with the same priority 
            # will maintain their original relative order (FIFO).
            self.jobs.sort(key=lambda x: x.priority, reverse=True)
            job = self.jobs.pop(0)
            return job.id, job.data
    ```

    Double checking `Job` fields: `id`, `data`, `priority`, `retries`.
    Double checking `RetryPolicy` init: `max_attempts: int = 4`.
    Double checking `RetryPolicy.run` return: `(success, attempts_made)`.
    Double checking `JobQueue` methods: `add_job`, `process_job`, `get_next_job`.
    Double checking `get_next_job` return type: `tuple[str, dict] | None`.

    Final check on `RetryPolicy.run`:
    If `max_attempts` is 4.
    Loop 1: try. fail. `attempts_made` becomes 1.
    Loop 2: try. fail. `attempts_made` becomes 2.
    Loop 3: try. fail. `attempts_made` becomes 3.
    Loop 4: try. fail. `attempts_made` becomes 4.
    Loop 5: `4 < 4` is false. exit.
    Return `False, 4`.
    Wait, if it succeeds on the 4th attempt:
    Loop 4: try. success. return `True, 4`.
    Looks correct.

    Wait, one detail in `JobQueue.add_job`. The prompt says `add_job(self, job_id: str, data: dict, priority: int = 0) -> None`.
    My implementation:
    `def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:`
    It adds to the list.

    Wait, if I `sort` inside `get_next_job`, and `process_job` is called, `process_job` finds the job by ID. This works.

    One tiny detail: `Job` dataclass `retries` field.
    The prompt says `retries: int = 0`. My code has it.
    Does `RetryPolicy` use the `retries` field in `Job`?
    Prompt says: "`RetryPolicy` — encapsulates retry-with-backoff... `run` calls `fn(data)`, retries... until success or `max_attempts` reached".
    It doesn't say to decrement `job.retries`. It just says `run` handles the retry logic based on `max_attempts`.
    So I will leave `job.retries` as is unless `RetryPolicy` is supposed to modify it. The instructions don't say to modify it, just to encapsulate the logic.

    Final structure:
    ```python
    from dataclasses import dataclass
    from typing import Callable, Any

    @dataclass
    class Job:
        id: str
        data: dict
        priority: int = 0
        retries: int = 0

    class RetryPolicy:
        def __init__(self, max_attempts: int = 4):
            self.max_attempts = max_attempts

        def run(self, fn: Callable, data: dict) -> tuple[bool, int]:
            attempts_made = 0
            while attempts_made < self.max_attempts:
                try:
                    fn(data)
                    return True, attempts_made + 1
                except Exception:
                    attempts_made += 1
            return False, attempts_made

    class JobQueue:
        def __init__(self):
            self.jobs: list[Job] = []
            self.retry_policy = RetryPolicy()

        def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
            self.jobs.append(Job(id=job_id, data=data, priority=priority)))

        def process_job(self, job_id: str, processor: Callable) -> bool:
            job = next((j for j in self.jobs if j.id == job_id), None)
            if job is None:
                return False
            
            success, _ = self.retry_policy.run(processor, job.data)
            return success

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self.jobs:
                return None
            
            # Use a stable sort to maintain FIFO for identical priorities
            self.jobs.sort(key=lambda x: x.priority, reverse=True)
            job = self.jobs.pop(0)
            return job.id, job.data
    ```
    Wait, I noticed a typo in my thinking: `self.jobs.append(Job(id=job_id, data=data, priority=priority)))` -> 3 closing brackets. Fixed.
</reasoning_content>

<content>
```python
from dataclasses import dataclass
from typing import Callable, Any

@dataclass
class Job:
    id: str
    data: dict
    priority: int = 0
    retries: int = 0

class RetryPolicy:
    def __init__(self, max_attempts: int = 4):
        self.max_attempts = max_attempts

    def run(self, fn: Callable, data: dict) -> tuple[bool, int]:
        attempts_made = 0
        while attempts_made < self.max_attempts:
            try:
                fn(data)
                return True, attempts_made + 1
            except Exception:
                attempts_made += 1
        return False, attempts_made

class JobQueue:
    def __init__(self):
        self.jobs: list[Job] = []
        self.retry_policy = RetryPolicy()

    def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
        self.jobs.append(Job(id=job_id, data=data, priority=priority)))

    def process_job(self, job_id: str, processor: Callable) -> bool:
        job = next((j for j in self.jobs if j.id == job_id), None)
        if job is None:
            return False
        
        success, _ = self.retry_policy.run(processor, job.data)
        return success

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.jobs:
            return None
        
        # Python's sort is stable, preserving FIFO order for items with the same priority
        self.jobs.sort(key=lambda x: x.priority, reverse=True)
        job = self.jobs.pop(0)
        return job.id, job.data
```
</content>