<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Extend a job queue with retry logic and exponential backoff.
    *   Requirements:
        1.  Add `process_job(job_id: str, processor: Callable) -> bool`.
        2.  Retry up to 3 times on exception.
        3.  Exponential backoff: 1s, 2s, 4s (simulated/stored, not necessarily `sleep`).
        4.  Track retry count for each job.
        5.  Return `True` on success, `False` on failure after retries.
    *   Input: Code snippet example provided.
    *   Output: Only Python code, no explanations.
    *   Context: The user implies there might be an existing `JobQueue` class or I need to define one based on the example usage (`queue = JobQueue()`, `queue.add_job(...)`). Since the prompt says "Extend the job queue", I should probably provide a complete class definition that includes the `add_job` method (implied by the example) and the new `process_job` method.

2.  **Determine Class Structure:**
    *   Class name: `JobQueue`.
    *   Methods needed:
        *   `__init__`: Initialize storage (e.g., dictionary for jobs).
        *   `add_job`: Store job details (id, data).
        *   `process_job`: The core logic requested.
    *   State needed:
        *   Jobs storage (id -> data).
        *   Retry tracking (id -> retry count).

3.  **Implement `process_job`:**
    *   Signature: `process_job(self, job_id: str, processor: Callable) -> bool`.
    *   Logic:
        *   Retrieve job data.
        *   Loop up to 3 retries (including the first attempt).
        *   Try calling `processor(data)`.
        *   If success, return `True`.
        *   If exception, increment retry count.
        *   If retries < max_retries, calculate backoff delay (1s, 2s, 4s). Store this backoff in the job state for tracking.
        *   If retries >= max_retries, return `False`.
    *   Backoff Logic:
        *   Attempt 1 (0 retries before): 0s (or immediate).
        *   Attempt 2 (1 retry left): 1s.
        *   Attempt 3 (2 retries left): 2s.
        *   Wait, the requirement says "retry up to 3 times". Usually, this means 1 attempt + 2 retries, or 3 total attempts. Let's interpret "retry up to 3 times" as "max 3 attempts total" or "max 3 retries".
        *   Looking at the backoff sequence: 1s, 2s, 4s. This implies 3 distinct backoff intervals.
        *   Sequence:
            *   Attempt 1 (Initial): If fails, wait 1s.
            *   Attempt 2: If fails, wait 2s.
            *   Attempt 3: If fails, wait 4s.
            *   Attempt 4: If fails, give up.
        *   So, max attempts = 4? Or does "retry up to 3 times" mean total attempts = 3?
        *   Standard interpretation: "Retry 3 times" often means 1 initial + 3 retries = 4 attempts. However, the backoff list provided is 1s, 2s, 4s (3 values). This matches 3 retries.
        *   Let's align with the specific backoff list: 1s, 2s, 4s.
        *   Attempt 1: Run. If fail -> Wait 1s.
        *   Attempt 2: Run. If fail -> Wait 2s.
        *   Attempt 3: Run. If fail -> Wait 4s.
        *   Attempt 4: Run. If fail -> Return False.
        *   This implies 4 attempts total.
        *   Alternative interpretation: "Retry up to 3 times" means max 3 *attempts* (Initial + 2 retries). Backoff list 1s, 2s, 4s might just be the formula $2^{n-1}$.
        *   Let's look at the wording: "retry up to 3 times".
        *   Scenario A: 1 initial + 3 retries = 4 executions. Backoffs: 1, 2, 4.
        *   Scenario B: 1 initial + 2 retries = 3 executions. Backoffs: 1, 2. (4s unused).
        *   Given the explicit list "1s, 2s, 4s", it strongly suggests 3 backoff intervals, which implies 3 retries after the initial attempt.
        *   I will implement 3 retries (Total 4 attempts).
        *   Wait, if I implement `range(max_retries)` where `max_retries = 3`, that usually means 3 loops.
        *   Let's stick to the specific backoff values provided: 1, 2, 4.
        *   Logic:
            *   Try 1.
            *   If fail, wait 1s.
            *   Try 2.
            *   If fail, wait 2s.
            *   Try 3.
            *   If fail, wait 4s.
            *   Try 4.
            *   If fail, return False.
        *   This feels like 3 *retries*.
        *   Let's check the constraint: "Track retry count for each job".
        *   I need to store `retry_count` in the job state.

4.  **Refining Logic:**
    *   Store jobs in a dict: `self.jobs = {}`.
    *   Store retry info: `self.retries = {}`.
    *   `process_job`:
        *   Get job data.
        *   Loop `attempt` in `range(4)` (1 initial + 3 retries).
        *   Inside loop:
            *   Call processor.
            *   If success, return True.
            *   If exception:
                *   Increment retry count.
                *   If `attempt < 3`:
                    *   Calculate backoff (2^attempt seconds? 1, 2, 4).
                    *   Store backoff in job metadata.
                    *   Continue.
                *   Else:
                    *   Return False.
    *   Wait, the prompt says "retry up to 3 times".
    *   If I loop 3 times for retries, that's 3 retries.
    *   Backoff values: 1, 2, 4.
    *   Let's assume `max_retries = 3`.
    *   Loop `i` from 0 to 2 (3 times).
    *   Wait `2^i` seconds.
    *   If exception, increment count.
    *   If count > max_retries, return False.
    *   Actually, simpler:
        *   `max_retries = 3`
        *   `for attempt in range(max_retries + 1):` (4 attempts total)
        *   Wait before retry.
    *   Let's keep it simple:
        *   `retry_count` starts at 0.
        *   `max_retries = 3`
        *   Loop:
            *   Try.
            *   Catch.
            *   `retry_count += 1`
            *   If `retry_count <= max_retries`:
                *   Wait `2**(retry_count - 1)` (1, 2, 4).
                *   Store wait time.
            *   Else:
                *   Return False.
    *   Wait, if I wait before the retry, the first wait is 1s (after 1st fail).
    *   Retry 1 (attempt 2): Wait 1s.
    *   Retry 2 (attempt 3): Wait 2s.
    *   Retry 3 (attempt 4): Wait 4s.
    *   This matches the requirement "1s, 2s, 4s".

5.  **Drafting Code:**
    *   Need to define `JobQueue` class.
    *   Need `add_job`.
    *   Need `process_job`.
    *   Need to store retry info.
    *   Store backoff logic.

    ```python
    import time

    class JobQueue:
        def __init__(self):
            self.jobs = {}
            self.retries = {}

        def add_job(self, job_id: str, data: dict):
            self.jobs[job_id] = data
            self.retries[job_id] = 0

        def process_job(self, job_id: str, processor: callable) -> bool:
            if job_id not in self.jobs:
                return False

            data = self.jobs[job_id]
            max_retries = 3
            current_retry = 0

            while current_retry <= max_retries:
                try:
                    processor(data)
                    return True
                except Exception as e:
                    current_retry += 1
                    self.retries[job_id] = current_retry
                    
                    if current_retry <= max_retries:
                        # Exponential backoff: 1s, 2s, 4s
                        delay = 2 ** (current_retry - 1)
                        self.retries[job_id] = {
                            "count": current_retry,
                            "delay": delay
                        }
                        # Simulate sleep
                        # time.sleep(delay) 
                    else:
                        return False
            return False
    ```
    Wait, `self.retries[job_id]` logic is a bit messy in the draft.
    Requirement 4: "Track retry count for each job".
    Requirement 3: "Use exponential backoff... (can be simulated)".
    I should track the delay associated with the next retry.

    Refined Logic:
    1.  Store jobs in `self.jobs`.
    2.  Store retry state in `self.retry_state`.
    3.  `process_job`:
        *   Check if job exists.
        *   Loop `attempt` from 0 to 3 (Total 4 attempts = 1 initial + 3 retries).
        *   If exception:
            *   If `attempt < 3`:
                *   Wait `2**attempt`.
                *   Increment retry counter.
            *   Else:
                *   Return False.
        *   If success:
            *   Return True.

    Let's align `2**attempt` with 1, 2, 4.
    Attempt 0 (Initial): Fail.
    Retry 1 (Before 2nd attempt): Wait `2**0` = 1s.
    Retry 2 (Before 3rd attempt): Wait `2**1` = 2s.
    Retry 3 (Before 4th attempt): Wait `2**2` = 4s.
    Retry 4 (Final): Fail. Return False.

    This means `max_retries = 3`.
    Code structure:
    ```python
    class JobQueue:
        def __init__(self):
            self.jobs = {}
            self.retry_counts = {}
            self.backoff_delays = {}

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

        def process_job(self, job_id: str, processor: callable) -> bool:
            if job_id not in self.jobs:
                return False

            data = self.jobs[job_id]
            max_retries = 3
            current_retry = 0

            while current_retry <= max_retries:
                try:
                    return processor(data)
                except Exception:
                    current_retry += 1
                    self.retry_counts[job_id] = current_retry
                    
                    if current_retry <= max_retries:
                        delay = 2 ** (current_retry - 1)
                        self.backoff_delays[job_id] = delay
                        # Simulated delay
                        # time.sleep(delay)
                    else:
                        return False
            return True # Should be caught by return inside loop
    ```
    Wait, the `while` loop logic is slightly redundant with the `try/except` inside.
    Simpler:
    ```python
    for attempt in range(max_retries + 1): # 0, 1, 2, 3 (4 attempts)
        try:
            processor(data)
            return True
        except Exception:
            if attempt < max_retries:
                delay = 2 ** attempt
                self.backoff_delays[job_id] = delay
                self.retry_counts[job_id] = attempt + 1
            else:
                return False
    ```
    Wait, `range(max_retries + 1)` where `max_retries=3` gives 0, 1, 2, 3.
    If `attempt=0` (1st try): Fail. `attempt < 3`. Delay `2^0 = 1`.
    If `attempt=1` (2nd try): Fail. `attempt < 3`. Delay `2^1 = 2`.
    If `attempt=2` (3rd try): Fail. `attempt < 3`. Delay `2^2 = 4`.
    If `attempt=3` (4th try): Fail. `attempt == 3`. Return False.
    This matches 1s, 2s, 4s backoff.
    Retry count tracking: `self.retry_counts[job_id] = attempt + 1`.
    Wait, if it succeeds on attempt 0, count is 0. If it fails 1st, count becomes 1.
    Requirement 4: "Track retry count for each job".
    I will store the number of retries performed.

    One detail: "Return True if job succeeded, False if all retries exhausted".
    If it succeeds on first try, return True.
    If it fails all 4 tries, return False.

    Let's refine the implementation to be clean.

    ```python
    class JobQueue:
        def __init__(self):
            self.jobs = {}
            self.retry_counts = {}

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

        def process_job(self, job_id: str, processor: callable) -> bool:
            if job_id not in self.jobs:
                return False
            
            data = self.jobs[job_id]
            max_retries = 3
            current_retry = 0
            
            for attempt in range(max_retries + 1):
                try:
                    processor(data)
                    return True
                except Exception:
                    current_retry += 1
                    self.retry_counts[job_id] = current_retry
                    
                    if current_retry <= max_retries:
                        # Exponential backoff: 1s, 2s, 4s
                        delay = 2 ** (current_retry - 1)
                        self.retry_counts[job_id] = current_retry
                        # Simulated storage of delay
                        self.jobs[job_id]['next_backoff'] = delay
                    else:
                        return False
            return False
    ```
    Wait, `self.jobs[job_id]['next_backoff']` modifies the job data. That's fine for tracking.
    Requirement 4 says "Track retry count for each job". `self.retry_counts` covers this.
    Requirement 3 says "Use exponential backoff... (can be simulated)". Storing the delay covers this.

    Wait, `current_retry` logic in the loop.
    `for attempt in range(max_retries + 1)`:
    Attempt 0: Fail. `current_retry` becomes 1. `current_retry <= 3`. Delay 1.
    Attempt 1: Fail. `current_retry` becomes 2. `current_retry <= 3`. Delay 2.
    Attempt 2: Fail. `current_retry` becomes 3. `current_retry <= 3`. Delay 4.
    Attempt 3: Fail. `current_retry` becomes 4. `current_retry <= 3` (False). Return False.
    This logic holds.

    Wait, I should check if `processor` is `Callable`. Type hinting is good.
    I need to make sure I don't import `time` unless I use `sleep`. The prompt says "can be simulated", so I can just store the delay value without actually sleeping. I will store the delay in the job metadata or a separate dict.
    I'll store `delay` in `self.jobs[job_id]` alongside data to keep it self-contained per job, or a separate dict. A separate dict `self.backoff` is cleaner.

    Revised Plan:
    1. Define `JobQueue` class.
    2. `__init__`: `self.jobs`, `self.retries`.
    3. `add_job`: Store data, init retry count to 0.
    4. `process_job`:
       - Check existence.
       - Loop `range(4)` (1 initial + 3 retries).
       - Try processor.
       - On exception:
         - Increment retry count.
         - If retries <= 3:
           - Calculate delay `2**(retry_count - 1)`.
           - Store delay in job metadata.
         - Else:
           - Return False.
       - On success:
         - Return True.
    5. Ensure type hints match requirement.

    Wait, one edge case: `max_retries = 3`.
    If I use `range(max_retries + 1)`, it iterates 0, 1, 2, 3.
    If `attempt` is 0 (1st try), fails. `retry_count` becomes 1. Delay `2**0 = 1`.
    If `attempt` is 1 (2nd try), fails. `retry_count` becomes 2. Delay `2**1 = 2`.
    If `attempt` is 2 (3rd try), fails. `retry_count` becomes 3. Delay `2**2 = 4`.
    If `attempt` is 3 (4th try), fails. `retry_count` becomes 4. Return False.
    This aligns with 1s, 2s, 4s.

    Wait, I need to make sure I don't confuse `attempt` index with retry count.
    Let's use explicit retry counter variable.

    ```python
    class JobQueue:
        def __init__(self):
            self.jobs = {}
            self.retry_counts = {}

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

        def process_job(self, job_id: str, processor: callable) -> bool:
            if job_id not in self.jobs:
                return False
            
            data = self.jobs[job_id]
            max_retries = 3
            retry_count = 0
            
            while retry_count <= max_retries:
                try:
                    return processor(data)
                except Exception:
                    retry_count += 1
                    self.retry_counts[job_id] = retry_count
                    
                    if retry_count <= max_retries:
                        delay = 2 ** (retry_count - 1)
                        self.jobs[job_id]['backoff'] = delay
                    else:
                        return False
            return False
    ```
    Wait, the `return processor(data)` inside `try` works if `processor` returns a value (like the example `requests.get`). If it just raises exceptions, it works too.
    However, `return processor(data)` returns whatever the processor returns. If it succeeds, `True` is required.
    The requirement says "Return True if job succeeded".
    The example shows:
    ```python
    def fetch_url(data):
        return requests.get(data["url"])
    ```
    If it succeeds, `process_job` should return `True`, not the response object.
    So I should wrap it:
    ```python
    try:
        processor(data)
        return True
    except Exception:
        ...
    ```
    This ensures `True` is returned on success regardless of processor's return value.

    Final check on constraints:
    1. `process_job(job_id: str, processor: Callable) -> bool`.
    2. Retry up to 3 times (implies 1 initial + 3 retries = 4 attempts).
    3. Backoff 1s, 2s, 4s.
    4. Track retry count.
    5. Return True/False.

    Code structure:
    ```python
    from typing import Callable, Any, Dict

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

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

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

            data = self.jobs[job_id]
            max_retries = 3
            retry_count = 0

            while retry_count <= max_retries:
                try:
                    processor(data)
                    return True
                except Exception:
                    retry_count += 1
                    self.retry_counts[job_id] = retry_count

                    if retry_count <= max_retries:
                        # Exponential backoff: 1s, 2s, 4s
                        delay = 2 ** (retry_count - 1)
                        self.jobs[job_id]['backoff'] = delay
                    else:
                        return False
            return False
    ```
    Wait, if `retry_count` reaches 4 (after 3 retries), `retry_count <= max_retries` is False, so it returns False.
    Wait, the loop condition `retry_count <= max_retries` allows `retry_count` to be 3.
    Iteration 1: `retry_count` = 0. Process. Fail. `retry_count` = 1. Delay 1.
    Iteration 2: `retry_count` = 1. Process. Fail. `retry_count` = 2. Delay 2.
    Iteration 3: `retry_count` = 2. Process. Fail. `retry_count` = 3. Delay 4.
    Iteration 4: `retry_count` = 3. Process. Fail. `retry_count` = 4. `4 <= 3` False. Return False.
    Iteration 5 (if loop continued): `retry_count` = 4. Process. Fail. `retry_count` = 5. Return False.
    Actually, if `retry_count` becomes 4 inside the loop, `retry_count <= max_retries` (4 <= 3) is False, so it returns False immediately.
    But wait, if I am in the loop `while retry_count <= max_retries`:
    Start: `retry_count` = 0.
    Fail. `retry_count` = 1. Delay 1.
    Loop check: 1 <= 3. True.
    Start: `retry_count` = 1.
    Fail. `retry_count` = 2. Delay 2.
    Loop check: 2 <= 3. True.
    Start: `retry_count` = 2.
    Fail. `retry_count` = 3. Delay 4.
    Loop check: 3 <= 3. True.
    Start: `retry_count` = 3.
    Fail. `retry_count` = 4. Delay logic skipped (else branch returns False).
    Loop check: 4 <= 3. False.
    End loop. Return False.
    Wait, inside the `except` block, if `retry_count > max_retries`, I return False immediately.
    So the `while` condition check at the top is redundant if I return False in the `else` branch of the `if`.
    But `while retry_count <= max_retries` ensures I don't process beyond max retries if I didn't return False inside.
    Actually, if I return False inside the except block when `retry_count > max_retries`, the loop terminates naturally.
    But I need to make sure I don't return False inside the except block if I still have retries left.
    My logic:
    ```python
    if retry_count <= max_retries:
        delay...
    else:
        return False
    ```
    If `retry_count` becomes 4 (after 3rd retry failure), `4 <= 3` is False. Returns False.
    So the loop doesn't need to check condition for the 4th fail, because I return False.
    But the loop condition `retry_count <= max_retries` allows entering the loop with `retry_count` = 3 (which is the 4th attempt).
    Attempt 1: count 0 -> 1.
    Attempt 2: count 1 -> 2.
    Attempt 3: count 2 -> 3.
    Attempt 4: count 3 -> 4.
    So `retry_count` represents "retries performed so far".
    If `retry_count` is 3, I have done 3 retries. I am attempting the 4th time (1st + 3 retries).
    If that fails, `retry_count` becomes 4.
    Then `4 <= 3` is False. Return False.
    This logic is correct.

    One small detail: `processor: Callable`. I should use `typing.Callable` or just `Callable`. I'll use `Callable` from `typing`.
    And I need to import `typing` or just use `typing` prefix if available. I'll import it.
    Actually, standard practice for these snippets is often just `from typing import ...` or implicit. I will add imports to be safe.
    Also, `data` type in `add_job` isn't strictly defined, but `dict` is implied by example.

    Wait, I should check if the user wants me to *extend* an existing code block or provide the full class.
    "Extend the job queue... Write only the Python code".
    Usually implies providing the full class definition so it can be run, as I don't see the original code in the prompt (only the example usage).
    I will provide the full `JobQueue` class.

    One check on "Track retry count for each job".
    My code stores `self.retry_counts[job_id]`.
    I should also make sure the `backoff` is stored somewhere accessible or just calculated.
    Requirement 3: "The backoff delays can be stored/tracked rather than actually sleeping."
    I am storing `self.jobs[job_id]['backoff']`. This satisfies "stored".

    Let's refine the code for clarity and strict adherence.

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

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

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

        def process_job(self, job_id: str, processor: Callable) -> bool:
            if job_id not in self.jobs:
                return False
            
            data = self.jobs[job_id]
            max_retries = 3
            retry_count = 0
            
            while retry_count <= max_retries:
                try:
                    processor(data)
                    return True
                except Exception:
                    retry_count += 1
                    self.retry_counts[job_id] = retry_count
                    
                    if retry_count <= max_retries:
                        delay = 2 ** (retry_count - 1)
                        self.jobs[job_id]['backoff'] = delay
                    else:
                        return False
            return False
    ```
    Wait, `while retry_count <= max_retries`:
    If `retry_count` is 0 (Start).
    Inside try: Fail.
    `retry_count` = 1.
    `1 <= 3`. Store delay 1.
    Loop checks `1 <= 3`.
    Inside try: Fail.
    `retry_count` = 2.
    `2 <= 3`. Store delay 2.
    Loop checks `2 <= 3`.
    Inside try: Fail.
    `retry_count` = 3.
    `3 <= 3`. Store delay 4.
    Loop checks `3 <= 3`.
    Inside try: Fail.
    `retry_count` = 4.
    `4 <= 3` (False).
    Else: Return False.
    Loop terminates.
    Return False.
    Wait, if I return False inside the except block, the `while` loop might not finish cleanly, but the function returns.
    However, if I don't return False inside the except block (e.g. if I had more retries), the loop continues.
    But here, if `retry_count > max_retries`, I return False immediately.
    So the `while` condition is effectively checked only when `retry_count <= max_retries` (loop continues).
    If `retry_count` becomes 4, I return False immediately in the `else`.
    So the loop condition `retry_count <= max_retries` isn't strictly necessary if the `else` returns False, but it's good for the initial check.
    Actually, if I return False in the `else`, I exit the function.
    So the `return False` at the end of the function is technically unreachable if `retry_count` exceeds max retries inside the `except`.
    But if `processor` succeeds, it returns True.
    So `return False` at the end is a fallback (e.g. if loop finishes without exception, though logic suggests it won't).
    Actually, if `retry_count` starts at 0.
    If `processor` fails 4 times.
    Iter 1: count 1.
    Iter 2: count 2.
    Iter 3: count 3.
    Iter 4: count 4. Else -> Return False.
    So the final `return False` is dead code if the `else` returns False.
    But I will keep it for completeness.

    Wait, one logic error in my trace:
    Iter 1: `retry_count` starts 0. Exception. `retry_count` -> 1. Store delay 1.
    Iter 2: `retry_count` starts 1. Exception. `retry_count` -> 2. Store delay 2.
    Iter 3: `retry_count` starts 2. Exception. `retry_count` -> 3. Store delay 4.
    Iter 4: `retry_count` starts 3. Exception. `retry_count` -> 4. Else -> Return False.
    This works.
    Wait, `delay = 2 ** (retry_count - 1)`.
    When `retry_count` is 1 (after 1st fail), delay = 1.
    When `retry_count` is 2 (after 2nd fail), delay = 2.
    When `retry_count` is 3 (after 3rd fail), delay = 4.
    This matches 1s, 2s, 4s.

    Final check on imports. `Callable` needs `typing`.
    I will write the code cleanly.

    One thing: The prompt asks to "Extend the job queue". It implies I should provide the code that *includes* the extension. I will provide the full class definition.

    Wait, I should ensure `processor` is called correctly.
    `processor(data)` is fine.
    Type hints: `job_id: str`, `processor: Callable`, `-> bool`.
    Data type in `add_job`: `data: dict` (implied). I'll use `Dict`.

    One detail: "The backoff delays can be stored/tracked rather than actually sleeping."
    I am storing `delay` in `self.jobs[job_id]['backoff']`.
    This satisfies the requirement.

    Wait, `retry_count` in `self.retry_counts` should reflect the number of retries *attempted*.
    If I succeed on the 3rd attempt (2 retries), `retry_count` should be 2.
    My code: `self.retry_counts[job_id] = retry_count`.
    If success happens, I return True. I don't update `retry_counts` on success.
    So `retry_counts` reflects retries done so far.
    If success on 1st try, `retry_counts` remains 0.
    If success on 2nd try, `retry_counts` is 1.
    This seems correct.

    One final check: "If the processor raises an exception, retry up to 3 times".
    My code retries up to 3 times (1st fail -> retry 1, 2nd fail -> retry 2, 3rd fail -> retry 3).
    Total attempts: 1 + 3 = 4.
    This matches the backoff list (3 backoffs).
    Code looks solid.

    Wait, I need to make sure I don't use `time.sleep` since it says "can be simulated".
    I will remove `import time` and `time.sleep`.
    I will just store the delay value.

    One detail on the example:
    ```python
    queue = JobQueue()
    queue.add_job("job1", {"url": "https://example.com"})
    ```
    My `add_job` signature matches `add_job(self, job_id: str, data: dict)`.
    The example passes `{"url": ...}` as `data`.
    My code expects `data` to be a dict.
    The `process_job` example passes `fetch_url`.
    My code expects `processor: Callable`.
    Everything matches.

    One final check on `retry_count` logic.
    If I use `retry_count` to track how many times we *failed*.
    Start 0.
    Fail 1. Count 1. Delay 1.
    Fail 2. Count 2. Delay 2.
    Fail 3. Count 3. Delay 4.
    Fail 4. Count 4. Return False.
    This means 3 retries were performed.
    Requirement: "retry up to 3 times".
    This means max 3 retries.
    So if it fails 3 times, we retry 3 times.
    If it fails 4 times, we exhausted retries.
    My logic: `retry_count` increments on exception.
    If `retry_count` becomes 4, it means 3 retries happened (after initial fail).
    Wait.
    Initial try. Fail. Retry 1. (Count 1).
    Retry 1. Fail. Retry 2. (Count 2).
    Retry 2. Fail. Retry 3. (Count 3).
    Retry 3. Fail. (Count 4).
    If I stop when `retry_count > 3`, I stop after 3 retries.
    My code: `if retry_count <= max_retries: ... else: return False`.
    When `retry_count` becomes 4, `4 <= 3` is False. Returns False.
    So it returns False immediately after the 3rd retry attempt fails (before the 4th execution).
    Wait.
    Iter 1 (Initial): Fail. Count 1. Delay 1.
    Iter 2 (Retry 1): Fail. Count 2. Delay 2.
    Iter 3 (Retry 2): Fail. Count 3. Delay 4.
    Iter 4 (Retry 3): Fail. Count 4. Else -> Return False.
    This means the 4th attempt (Retry 3) is made. If it fails, we return False.
    So we executed the processor 4 times.
    Retries performed: 3.
    This matches "retry up to 3 times".
    Okay, logic holds.

    Wait, one nuance.
    If I return False in the `except` block when `retry_count > max_retries`.
    Does the processor get called for the 4th time?
    Yes, because the check is in the `except` block.
    So the `try` block runs 4 times.
    1st run (Initial).
    2nd run (Retry 1).
    3rd run (Retry 2).
    4th run (Retry 3).
    If 4th run fails, `retry_count` increments to 4. `4 <= 3` False. Return False.
    So we do 4 runs. 3 Retries.
    This is correct.

    Final check on variable names and types.
    `job_id: str`
    `processor: Callable`
    `-> bool`
    `add_job` signature matches example.

    I will write the code now.

</reasoning_content>

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

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

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

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

        data = self.jobs[job_id]
        max_retries = 3
        retry_count = 0

        while retry_count <= max_retries:
            try:
                processor(data)
                return True
            except Exception:
                retry_count += 1
                self.retry_counts[job_id] = retry_count
                
                if retry_count <= max_retries:
                    # Exponential backoff: 1s, 2s, 4s
                    delay = 2 ** (retry_count - 1)
                    self.jobs[job_id]['backoff'] = delay
                else:
                    return False
        return False
```
</content>