<reasoning_content>
*   Goal: Extend a `JobQueue` class with a `process_job` method.
    *   Method Signature: `process_job(job_id: str, Processor: Callable) -> bool`.
    *   Context: `JobQueue` contains jobs added via `add_job(job_id, data)`.
    *   Behavior:
        1.  Call `processor(data)`.
        2.  Retry on exception. Max 4 attempts total.
        3.  Track retry count per job.
        4.  Exponential backoff delays (1s, 2s, 4s).
        5.  *Constraint:* Do NOT actually sleep (`time.sleep`). Record the delays in the data.
        6.  Return `True` on success, `False` on total failure.
        7.  Constraint: Standard library only. No extra packages.
        8.  Output format: Python code only, no explanations.

    *   The prompt doesn't provide the base `JobQueue` class, so I need to define a minimal version to make the code runnable/complete, but the primary focus is the extension.
    *   Structure of `JobQueue`:
        *   `jobs`: a dictionary mapping `job_id` to `data` (dict).
        *   `retry_counts`: a dictionary mapping `job_id` to count.

    *   `process_job(job_id, processor)`:
        *   Get `data` from `self.jobs`.
        *   Try to call `processor(data)`.
        *   If success -> return True.
        *   If exception:
            *   Check if attempts < 4.
            *   Calculate delay (2^(attempt-1)).
            *   Update `data` with the delay.
            *   Increment count.
            *   Recurse or loop? A loop is usually cleaner for retries.
            *   If failed 4 times -> return False.

    *   Retry 1 (initial): count 0.
    *   Retry 2: count 1, delay $2^0 = 1$.
    *   Retry 3: count 2, delay $2^1 = 2$.
    *   Retry 4: count 3, delay $2^2 = 4$.
    *   Total attempts: 4.

    ```python
    from typing import Callable, Dict, Any

    class JobQueue:
        def __init__(self):
            self.jobs: Dict[str, Dict[str, Any]] = {}
            self.retry_counts: Dict[str, int] = {}

        def add_job(self, job_id: str, data: Dict[str, Any]):
            self.jobs[job_id] = data
            self.retry_counts[job_id] = 0

        def process_job(self, job_id: str, processor: Callable[[Dict[str, Any]], Any]) -> bool:
            if job_id not in self.jobs:
                return False

            data = self.jobs[job_id]
            max_attempts = 4

            for attempt in range(max_attempts):
                try:
                    processor(data)
                    return True
                except Exception:
                    if attempt < max_attempts - 1:
                        # Exponential backoff: 1s, 2s, 4s
                        # attempt 0 (initial) fails -> next delay is 2^0 = 1
                        # attempt 1 fails -> next delay is 2^1 = 2
                        # attempt 2 fails -> next delay is 2^2 = 4
                        delay = 2 ** attempt
                        data['next_retry_delay'] = delay
                        self.retry_counts[job_id] += 1
                    else:
                        # Failed all attempts
                        break
            return False
    ```

    *   Wait, the prompt says "Track the retry count per job". I should ensure `retry_counts` is updated.
    *   "Do NOT actually sleep — record the delays as data."
    *   The prompt asks to "Extend the JobQueue class", implying I should show the class including the new method.

    *   Use `Callable` from `typing`.
    *   The logic for delays:
        *   Attempt 1 (initial): fails. `attempt` = 0. `delay` = $2^0 = 1$.
        *   Attempt 2: fails. `attempt` = 1. `delay` = $2^1 = 2$.
        *   Attempt 3: fails. `attempt` = 2. `delay` = $2^2 = 4$.
        *   Attempt 4: fails. Loop ends.

    Wait, if I use `range(4)`, `attempt` goes 0, 1, 2, 3.
    - `attempt` 0 fails: `delay` = $2^0=1$.
    - `attempt` 1 fails: `delay` = $2^1=2$.
    - `attempt` 2 fails: `delay` = $2^2=4$.
    - `attempt` 3 fails: `attempt` is 3. If I check `attempt < 3`, it won't record the delay for the "next" attempt because there is no next attempt. This aligns with the requirement.

    *   Requirement 1: Call `processor(data)`.
    *   Requirement 2: Retry on exception, 4 total attempts.
    *   Requirement 3: Track retry count.
    *   Requirement 4: Exponential backoff 1s, 2s, 4s (no sleep).
    *   Requirement 5: Return True/False.
    *   Requirement 6: Standard library only.

    One small detail: Should `retry_counts` be updated *before* or *after* the exception? Usually, if it fails, the count increases.

    ```python
    from typing import Callable, Dict, Any

    class JobQueue:
        def __init__(self):
            self.jobs: Dict[str, Dict[str, Any]] = {}
            self.retry_counts: Dict[str, int] = {}

        def add_job(self, job_id: str, data: Dict[str, Any]):
            self.jobs[job_id] = data
            self.retry_counts[job_id] = 0

        def process_job(self, job_id: str, processor: Callable[[Dict[str, Any]], Any]) -> bool:
            if job_id not in self.jobs:
                return False
            
            data = self.jobs[job_id]
            
            for attempt in range(4):
                try:
                    processor(data)
                    return True
                except Exception:
                    if attempt < 3:
                        delay = 2 ** attempt
                        data['next_retry_delay'] = delay
                        self.retry_counts[job_id] += 1
                    else:
                        break
            return False
    ```

    One edge case: If `job_id` is not in `self.jobs`, should it return False? The prompt doesn't specify, but it's good practice.

    Wait, the prompt says "Track the retry count per job". It doesn't say where to store it, but having a `retry_counts` dict or putting it inside `data` is standard. Since `add_job` takes `data`, and I need to record `delay` in `data`, I'll stick to the `jobs` dict for data and a separate dict for counts, or just update `data`. Let's stick to a dictionary for `retry_counts` to keep it clean.

    Actually, looking at Requirement 3: "Track the retry count per job."
    I'll initialize `retry_counts` in `__init__`.

    Wait, looking at Requirement 1: `data` is the dict passed to `add_job`.
    I should make sure I don't modify the original input if not intended, but usually, in these tasks, modifying the dictionary object is expected when asked to "record the delays as data".

    Final check on backoff:
    Attempt 1 (idx 0): fails. delay = $2^0 = 1$. Count = 1.
    Attempt 2 (idx 1): fails. delay = $2^1 = 2$. Count = 2.
    Attempt 3 (idx 2): fails. delay = $2^2 = 4$. Count = 3.
    Attempt 4 (idx 3): fails. Loop ends.
    This fits "initial + 3 retries".

    Wait, if I count "Retry Count", is the "initial" attempt count 0?
    If initial fails, retry count becomes 1.
    If retry 1 fails, retry count becomes 2.
    If retry 2 fails, retry count becomes 3.
    If retry 3 fails, we stop.
    Yes, that's 3 retries.

    Refining the code to be as concise as possible.

    ```python
    from typing import Callable, Dict, Any

    class JobQueue:
        def __init__(self):
            self.jobs: Dict[str, Dict[str, Any]] = {}
            self.retry_counts: Dict[str, int] = {}

        def add_job(self, job_id: str, data: Dict[str, Any]):
            self.jobs[job_id] = data
            self.retry_counts[job_id] = 0

        def process_job(self, job_id: str, processor: Callable[[Dict[str, Any]], Any]) -> bool:
            if job_id not in self.jobs:
                return False
            
            data = self.jobs[job_id]
            
            for attempt in range(4):
                try:
                    processor(data)
                    return True
                except Exception:
                    if attempt < 3:
                        delay = 2 ** attempt
                        data['next_retry_delay'] = delay
                        self.retry_counts[job_id] += 1
                    else:
                        break
            return False
    ```

    One detail: "Return True on the first successful attempt."
    The loop returns True immediately on success.

    Let's double check the `delay` logic.
    Attempt 0: Fail -> delay $2^0=1$.
    Attempt 1: Fail -> delay $2^1=2$.
    Attempt 2: Fail -> delay $2^2=4$.
    Attempt 3: Fail -> no more retries.
    This matches the "1s, 2s, 4s" sequence perfectly.

    One more thing: `Callable` type hint.
    `from typing import Callable` is required.

    Actually, I should make sure the `jobs` dictionary stores the reference so that modifications to `data` inside `process_job` persist in `self.jobs`. Since dictionaries are mutable, `data = self.jobs[job_id]` followed by `data['key'] = val` will update the object inside `self.jobs`.

    Final check on requirement: "Return False if all 4 attempts raise."
    If it reaches the end of the loop (after 4 attempts), it returns False. Correct.

    Let's refine the `retry_counts` storage. I'll include it in `__init__`.
</reasoning_content>

<content>
```python
from typing import Callable, Dict, Any

class JobQueue:
    def __init__(self):
        self.jobs: Dict[str, Dict[str, Any]] = {}
        self.retry_counts: Dict[str, int] = {}

    def add_job(self, job_id: str, data: Dict[str, Any]):
        self.jobs[job_id] = data
        self.retry_counts[job_id] = 0

    def process_job(self, job_id: str, processor: Callable[[Dict[str, Any]], Any]) -> bool:
        if job_id not in self.jobs:
            return False
        
        data = self.jobs[job_id]
        
        for attempt in range(4):
            try:
                processor(data)
                return True
            except Exception:
                if attempt < 3:
                    # Exponential backoff: 2^0=1, 2^1=2, 2^2=4
                    delay = 2 ** attempt
                    data['next_retry_delay'] = delay
                    self.retry_counts[job_id] += 1
                else:
                    break
                    
        return False
```
</content>