<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` method.
        2.  Retry up to 3 times on exception.
        3.  Exponential backoff: 1s, 2s, 4s (simulated tracking).
        4.  Track retry count per job.
        5.  Return `True` on success, `False` on exhaustion.
    *   Example provided shows usage (`queue.add_job`, `queue.process_job`).
    *   Constraint: Write *only* Python code, no explanations.

2.  **Determine Existing Structure (Implicit):**
    *   The prompt implies a `JobQueue` class exists or needs to be created.
    *   It shows `queue.add_job("job1", {"url": "https://example.com"})`.
    *   It shows `queue.process_job("job1", fetch_url)`.
    *   I need to implement the `JobQueue` class including `add_job` and `process_job`.

3.  **Drafting the Logic:**
    *   Class `JobQueue`.
    *   Internal storage for jobs (dict mapping `job_id` to `data`).
    *   Internal storage for retry metadata (dict mapping `job_id` to `retry_count`).
    *   `add_job(job_id, data)`: Store job data.
    *   `process_job(job_id, processor)`:
        *   Check if job exists.
        *   Loop up to max_retries (3).
        *   Inside loop:
            *   Try calling `processor`.
            *   If success: return `True`.
            *   If exception:
                *   Increment retry count.
                *   If retries < max_retries:
                    *   Calculate backoff (1, 2, 4).
                    *   Store backoff info (optional, but requirement says "can be simulated" and "backoff delays can be stored/tracked").
                    *   Continue loop.
                *   Else:
                    *   Return `False`.
    *   Wait, the requirement says "Retry up to 3 times". Usually, this means 1 initial attempt + 3 retries = 4 total attempts, or 3 total attempts including the first one.
    *   Looking at the backoff sequence: 1s, 2s, 4s. This implies 3 retry intervals.
    *   Interpretation:
        *   Attempt 1 (Initial): No wait.
        *   Fail -> Retry 1 (Wait 1s).
        *   Fail -> Retry 2 (Wait 2s).
        *   Fail -> Retry 3 (Wait 4s).
        *   Fail -> Stop (Total 4 attempts).
    *   However, often "retry up to 3 times" means `max_retries = 3`.
    *   Let's align with the backoff list: [1, 2, 4]. This suggests 3 retry opportunities.
    *   So:
        *   Attempt 1.
        *   If fail, wait 1s, Attempt 2.
        *   If fail, wait 2s, Attempt 3.
        *   If fail, wait 4s, Attempt 4.
        *   If fail, stop.
    *   Requirement 3 says "1s, 2s, 4s". This matches 3 retry intervals.
    *   Requirement 2 says "retry up to 3 times".
    *   So logic: `max_retries = 3`. `retry_count` tracks how many times we've retried.
    *   Wait, tracking "retry count for each job" implies persistence or state across calls? The example shows `process_job` being called once per job ID.
    *   If `process_job` is called multiple times for the same ID, should it reset? Usually, `process_job` handles the execution. I will assume it manages the state for that specific execution or stores metadata.
    *   Given the example `queue.add_job` then `queue.process_job`, it implies `process_job` executes the work.
    *   To track retry count *per job*, I need a dictionary `self.retry_counts`.
    *   To handle the backoff logic, I need a list of delays `[1, 2, 4]`.

4.  **Refining the Implementation:**
    *   Class `JobQueue`.
    *   `__init__`: `self.jobs = {}`, `self.retry_counts = {}`, `self.backoff_delays = [1, 2, 4]`.
    *   `add_job`: `self.jobs[job_id] = data`.
    *   `process_job`:
        *   Get job data.
        *   Get current retry count (default 0).
        *   Loop:
            *   Try process.
            *   If success: reset retry count? Return True.
            *   If exception:
                *   Increment retry count.
                *   If retry count <= len(backoff_delays) (i.e., <= 3):
                    *   Wait (simulate).
                    *   Continue.
                *   Else:
                    *   Return False.
    *   Wait, strict interpretation: "Retry up to 3 times".
    *   If I retry 3 times, that means 1 initial + 3 retries = 4 calls.
    *   Backoff: 1, 2, 4. That's 3 delays.
    *   So:
        *   Call 1 (Retry 0). Fail.
        *   Wait 1. Call 2 (Retry 1). Fail.
        *   Wait 2. Call 3 (Retry 2). Fail.
        *   Wait 4. Call 4 (Retry 3). Fail.
        *   Stop.
    *   So `max_retries` is 3. `backoff_delays` has length 3.
    *   Logic: `for attempt in range(max_retries + 1):`. Inside loop: try/except. If except: if `attempt < max_retries`: sleep `backoff[attempt]`.
    *   Wait, the requirement says "Track retry count for each job". This suggests state might need to persist if `process_job` is called again? Or just internal state during the `process_job` call?
    *   Given `process_job(job_id, processor)` signature, it looks like a single invocation to process the job.
    *   I will store `retry_counts` in the queue instance to persist across invocations if needed, or just use local logic. To satisfy "Track retry count for each job", I should probably store it in `self.retry_counts` keyed by `job_id`.
    *   When `process_job` is called, check `self.retry_counts.get(job_id, 0)`.
    *   Actually, usually `process_job` implies the full execution attempt. If I call it again later, does it resume?
    *   Simplest interpretation: `process_job` handles the retry logic internally in one go. The "retry count" is just metadata tracked during that process.
    *   However, "Track retry count for each job" suggests `self.retry_counts[job_id] = count`.
    *   Let's implement `process_job` to loop internally.
    *   Wait, if I call `process_job` once, it returns True/False.
    *   If I call it again on the same job_id, should it start over?
    *   Let's assume `process_job` runs the full retry logic in one go.
    *   To track retries *per job* across multiple `process_job` calls (e.g. if the job is not cleared), I should store it. But typically, a job is processed once.
    *   Let's stick to the internal state for the specific execution flow.
    *   Actually, looking at the example: `success = queue.process_job("job1", fetch_url)`. It looks like a fire-and-forget or immediate processing.
    *   I will store `retry_counts` in the queue to satisfy the requirement explicitly.
    *   Backoff delays: `[1, 2, 4]`.

5.  **Coding Details:**
    *   Imports: `time` (for `sleep` simulation), `Callable`.
    *   Class structure.
    *   `add_job` method.
    *   `process_job` method.
    *   Retry logic:
        *   `max_retries = 3`
        *   `delays = [1, 2, 4]`
        *   Loop `i` from 0 to `max_retries`.
        *   If `i > 0`: `time.sleep(delays[i-1])` (Wait before retry).
        *   Try `processor`.
        *   If success: return True.
        *   If fail:
            *   Update retry count.
            *   If `i < max_retries`: continue.
            *   Else: return False.
    *   Wait, requirement says "Retry up to 3 times".
    *   If `max_retries = 3`.
    *   Attempt 1.
    *   Fail -> Retry 1 (Wait 1).
    *   Fail -> Retry 2 (Wait 2).
    *   Fail -> Retry 3 (Wait 4).
    *   Fail -> Stop.
    *   Total attempts = 4.
    *   My loop logic `for attempt in range(max_retries + 1)` works.
    *   Inside loop:
        *   If `attempt > 0`: sleep `delays[attempt - 1]`.
        *   Try: process. Return True.
        *   Except:
            *   Increment retry count.
            *   If `attempt == max_retries`: return False.
    *   Wait, if I sleep *before* the retry, the 1s sleep happens before Attempt 2.
    *   Attempt 0: Process. Fail.
    *   Attempt 1: Sleep 1s. Process. Fail.
    *   Attempt 2: Sleep 2s. Process. Fail.
    *   Attempt 3: Sleep 4s. Process. Fail.
    *   This matches 3 retries with delays 1, 2, 4.

6.  **Constraint Check:**
    *   "Write only the Python code, no explanations."
    *   Need to include imports.
    *   Need to make sure `job_id` handling is robust.

7.  **Final Code Structure:**
    ```python
    import time
    from typing import Callable, Any, Dict

    class JobQueue:
        def __init__(self):
            self.jobs: Dict[str, Any] = {}
            self.retry_counts: Dict[str, int] = {}
            self.backoff_delays = [1, 2, 4] # 1s, 2s, 4s
            self.max_retries = 3

        def add_job(self, job_id: str, data: 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

            current_count = self.retry_counts.get(job_id, 0)
            # If we just process it, do we reset?
            # Let's assume process_job handles the full lifecycle or continues from state.
            # To keep it simple based on example: process_job runs the logic.
            # However, "Track retry count for each job" implies we store it.
            # Let's reset to 0 at start of process_job for a fresh run?
            # Or increment on failure.
            # Let's increment on failure.
            
            # Actually, if I call process_job, it should attempt.
            # Let's track retries within this call.
            
            # Reset count for this specific process_job execution logic?
            # Or persist? "Track retry count for each job" usually means metadata.
            # I will persist it in self.retry_counts.
            # Reset to 0 at start of processing?
            # Let's assume process_job is the entry point for execution.
            # So start with 0 retries for this execution.
            
            retries = 0
            attempts = 0
            max_attempts = self.max_retries + 1 # 1 initial + 3 retries
            
            while attempts < max_attempts:
                try:
                    processor(self.jobs[job_id])
                    self.retry_counts[job_id] = retries
                    return True
                except Exception:
                    retries += 1
                    attempts += 1
                    
                    if retries <= len(self.backoff_delays):
                        delay = self.backoff_delays[retries - 1]
                        time.sleep(delay)
                    else:
                        return False
            
            self.retry_counts[job_id] = retries
            return False
    ```
    *   Wait, the requirement says "Retry up to 3 times".
    *   If I fail 3 times, I return False.
    *   My logic: `retries` increments on fail.
    *   If `retries` reaches 3, I sleep (for 1st, 2nd retry) then try.
    *   Let's trace:
        *   Fail 1 (Attempt 0). Retries = 1. Sleep 1.
        *   Fail 2 (Attempt 1). Retries = 2. Sleep 2.
        *   Fail 3 (Attempt 2). Retries = 3. Sleep 4.
        *   Fail 4 (Attempt 3). Retries = 4. Return False.
    *   This is 3 retries + 1 initial = 4 attempts.
    *   Backoff: 1, 2, 4.
    *   This matches the requirement "1s, 2s, 4s".
    *   Wait, `self.retry_counts` update.
    *   Requirement: "Track retry count for each job".
    *   I should update `self.retry_counts[job_id]` on failure.
    *   Also need to handle `add_job` initialization.

    *   Refined Loop:
        *   `for attempt in range(4):` (0 to 3).
        *   If `attempt > 0`: `time.sleep(self.backoff_delays[attempt - 1])`.
        *   Try: process. Return True.
        *   Except:
            *   `self.retry_counts[job_id] = attempt` (or similar).
            *   If `attempt == 3`: return False.

    *   Wait, `add_job` example: `queue.add_job("job1", ...)`.
    *   `process_job` example: `queue.process_job("job1", fetch_url)`.
    *   I should ensure `add_job` initializes the retry counter.

    *   One detail: "If the processor raises an exception, retry up to 3 times".
    *   This usually means: Initial attempt + 3 retries.
    *   So `range(4)` is correct for attempts.
    *   Backoff happens *between* retries.
    *   So before Retry 1, wait 1s. Before Retry 2, wait 2s. Before Retry 3, wait 4s.
    *   Code:
        ```python
        for i in range(4):
            if i > 0:
                time.sleep(self.backoff_delays[i-1])
            try:
                ...
            except:
                if i == 3: return False
        ```
    *   This works.

    *   Wait, `self.retry_counts` needs to be updated.
    *   If success, what is the count? 0 retries used?
    *   If fail, what is the count?
    *   I will store the number of retries attempted.

    *   Wait, does `process_job` reset the retry count?
    *   The example doesn't show calling `process_job` multiple times.
    *   I will reset `retry_counts` to 0 when `add_job` is called, or track attempts within `process_job`.
    *   To be safe and follow "Track retry count for each job", I'll maintain `self.retry_counts` updated on failure.
    *   If success, I might reset it for the next time `process_job` is called, or leave it.
    *   Let's keep it simple: `process_job` manages its own state for the execution, but updates the persistent count.
    *   Actually, simpler: `self.retry_counts` stores how many times it has been retried *so far*.
    *   If `process_job` succeeds, we might consider the job done.
    *   I will reset count to 0 on successful completion.

    *   Wait, I need to check if the job exists in `self.jobs`.
    *   The example shows `queue.add_job` then `queue.process_job`.
    *   I need to define `JobQueue` class.

    *   Re-reading: "Write only the Python code".
    *   I need to make sure imports are included.
    *   I need to make sure the class name matches the usage `queue = JobQueue()`.

    *   Let's refine the retry logic to match "Retry up to 3 times" exactly.
    *   Attempt 1 (No wait).
    *   Fail.
    *   Wait 1s.
    *   Attempt 2 (Retry 1).
    *   Fail.
    *   Wait 2s.
    *   Attempt 3 (Retry 2).
    *   Fail.
    *   Wait 4s.
    *   Attempt 4 (Retry 3).
    *   Fail.
    *   Return False.
    *   Success on any attempt -> Return True.

    *   Implementation:
        ```python
        class JobQueue:
            def __init__(self):
                self.jobs = {}
                self.retry_counts = {}
                self.backoff = [1, 2, 4]
            
            def add_job(self, job_id, data):
                self.jobs[job_id] = data
                self.retry_counts[job_id] = 0
            
            def process_job(self, job_id, processor):
                if job_id not in self.jobs:
                    return False
                
                retries = 0
                max_retries = 3
                
                while retries <= max_retries:
                    try:
                        processor(self.jobs[job_id])
                        self.retry_counts[job_id] = retries
                        return True
                    except Exception:
                        retries += 1
                        self.retry_counts[job_id] = retries
                        
                        if retries <= max_retries:
                            time.sleep(self.backoff[retries - 1])
                        else:
                            return False
                return False
        ```
    *   Wait, logic check:
        *   `while retries <= max_retries`:
            *   Start: retries=0.
            *   Try process. Success -> Return True.
            *   Fail -> retries=1.
            *   If 1 <= 3: sleep backoff[0] (1s).
            *   Loop.
            *   Try process. Success -> Return True.
            *   Fail -> retries=2.
            *   If 2 <= 3: sleep backoff[1] (2s).
            *   Loop.
            *   Try process. Success -> Return True.
            *   Fail -> retries=3.
            *   If 3 <= 3: sleep backoff[2] (4s).
            *   Loop.
            *   Try process. Success -> Return True.
            *   Fail -> retries=4.
            *   If 4 <= 3: False. Else return False.
            *   Loop condition `4 <= 3` is False. Loop ends.
            *   Return False at end.
        *   This logic works.
        *   Wait, `while retries <= max_retries` allows `retries` to go to 4 inside the loop?
        *   If `retries` becomes 4 inside loop (after 4th fail).
        *   Next iteration check `4 <= 3` -> False.
        *   So it exits loop.
        *   Return False.
        *   This covers 4 attempts (1 initial + 3 retries).
        *   Wait, if `retries` is 4 inside the loop (after 4th fail), it increments, then checks condition for next loop.
        *   Wait, if 4th attempt fails:
            *   retries becomes 4.
            *   Sleep (if condition met).
            *   Next loop check `4 <= 3` -> False.
            *   Returns False.
        *   Wait, if 4th attempt fails, we already slept before the 4th attempt?
        *   No, `time.sleep` happens *after* fail, *before* next retry.
        *   Retry 1 (Fail) -> Sleep 1 -> Retry 2.
        *   Retry 2 (Fail) -> Sleep 2 -> Retry 3.
        *   Retry 3 (Fail) -> Sleep 4 -> Retry 4.
        *   Retry 4 (Fail) -> Stop.
        *   My code:
            *   Start retries=0.
            *   Fail. retries=1. Sleep 1.
            *   Fail. retries=2. Sleep 2.
            *   Fail. retries=3. Sleep 4.
            *   Fail. retries=4.
            *   Loop check `4 <= 3` -> False.
            *   Return False.
        *   This works.
        *   Wait, `self.retry_counts[job_id] = retries` is updated.
        *   If success, it saves the count.
        *   If fail, it saves the count.
        *   If I call `process_job` again?
        *   It should probably reset or continue. Given the example, it implies a single run.
        *   I'll assume `process_job` is the full lifecycle.
        *   I'll add `self.retry_counts[job_id] = 0` at start of `process_job` to ensure it tracks *this* execution's retries.
        *   Or keep the requirement "Track retry count for each job" as persistent state.
        *   I'll reset to 0 on `add_job` and increment on failure within `process_job`.
        *   If `process_job` is called again, does it continue from where it left off?
        *   Usually, `process_job` implies "Process this job".
        *   I will reset `retry_counts` to 0 inside `process_job` or rely on `add_job`.
        *   To be safe, I will reset `retry_counts` to 0 when `process_job` starts, or simply track the current execution's retries.
        *   Let's just update `self.retry_counts` on each failure.
        *   If I want to support re-trying a failed job later, I shouldn't reset.
        *   But typically `process_job` runs the full logic.
        *   I'll just reset `retry_counts` to 0 at the start of `process_job` to ensure it reflects the current run's status, as per the example flow. Or just track it.
        *   Actually, "Track retry count for each job" suggests a property of the job.
        *   I will update `self.retry_counts` when retries happen.
        *   I will reset `self.retry_counts[job_id] = 0` in `add_job`.
        *   Inside `process_job`, I will increment it on failure.
        *   If success, I might reset it or leave it. I'll leave it as the count of retries used in that successful run.

    *   Wait, `process_job` signature in example: `queue.process_job("job1", fetch_url)`.
    *   The processor is passed as an argument.
    *   So I don't need to store the processor in the queue.
    *   My code above handles this.

    *   One small fix: `import time` is needed.
    *   `Callable` type hint is needed for `processor`.
    *   `typing` import.

    *   Let's double check the backoff logic.
    *   "1s, 2s, 4s".
    *   My code uses `self.backoff[retries - 1]`.
    *   retries=1 -> index 0 (1s).
    *   retries=2 -> index 1 (2s).
    *   retries=3 -> index 2 (4s).
    *   retries=4 -> index 3 (Out of bounds).
    *   My loop condition `retries <= max_retries` (4 <= 3) prevents entering loop for retries=4.
    *   Wait.
    *   If retries=3 (after 3rd fail).
    *   Sleep 4s.
    *   Next loop retries=3. Process.
    *   If fail. retries=4.
    *   Loop check `4 <= 3` -> False.
    *   Return False.
    *   This means no sleep after 4th fail (correct, no more retries).
    *   This means sleep before 4th attempt (index 2, value 4s).
    *   This matches the requirement "1s, 2s, 4s" for retries 1, 2, 3.
    *   Wait, "Retry up to 3 times".
    *   Does this mean sleep 1s, Sleep 2s, Sleep 4s?
    *   Yes.
    *   So before Retry 1, Sleep 1.
    *   Before Retry 2, Sleep 2.
    *   Before Retry 3, Sleep 4.
    *   My code:
        *   Fail 1 (Initial). retries=1. Sleep 1.
        *   Fail 2 (Retry 1). retries=2. Sleep 2.
        *   Fail 3 (Retry 2). retries=3. Sleep 4.
        *   Fail 4 (Retry 3). retries=4.
    *   This aligns.

    *   Wait, I should check if `time` is imported.
    *   I will add `import time`.
    *   I will remove type hints if not strictly necessary, but the prompt example uses `job_id: str`. I should include types for correctness.

    *   Wait, `add_job` signature in example: `queue.add_job("job1", {"url": "https://example.com"})`.
    *   My `add_job` implementation matches.
    *   `process_job` signature: `queue.process_job("job1", fetch_url)`.
    *   My `process_job` implementation matches.

    *   Final check on constraints: "Write only the Python code, no explanations."
    *   I will provide the class definition.

    *   Wait, one detail: "Extend the job queue".
    *   Does this mean I should provide the whole class or just the extension?
    *   Since the example instantiates `JobQueue`, I should provide the full class definition so it's runnable.
    *   I will provide the full class.

    *   Wait, "Track retry count for each job".
    *   I should ensure `self.retry_counts` is accessible or updated.
    *   I'll ensure it's updated in the code.

    *   One logic correction:
    *   `while retries <= max_retries`:
    *   If `retries` starts at 0.
    *   Fail -> `retries` = 1. `time.sleep`.
    *   ...
    *   If `retries` becomes 4.
    *   Loop check `4 <= 3` -> False.
    *   Returns False.
    *   This works.
    *   But wait, if `retries` is 3 (3rd retry).
    *   Loop check `3 <= 3` -> True.
    *   Process.
    *   Fail -> `retries` = 4.
    *   Loop check `4 <= 3` -> False.
    *   Return False.
    *   This works.
    *   Wait, what if `retries` = 3 (after 3rd fail).
    *   We sleep 4s.
    *   Then loop.
    *   Then process.
    *   Then fail.
    *   `retries` = 4.
    *   Loop check.
    *   Return False.
    *   This covers 3 retries (1st, 2nd, 3rd) + Initial.
    *   Total 4 attempts.
    *   Correct.

    *   Wait, `self.retry_counts` update.
    *   If success, I return True.
    *   I should update `self.retry_counts[job_id] = retries` before returning True.
    *   If fail, I update `self.retry_counts[job_id] = retries` before sleeping or returning.
    *   I'll update it consistently.

    *   Wait, `add_job` should reset `retry_counts` to 0?
    *   Yes, `self.retry_counts[job_id] = 0`.

    *   Wait, `process_job` might be called multiple times for the same job?
    *   If so, should it reset?
    *   I'll reset to 0 at the start of `process_job` to ensure it counts retries for the *current* execution attempt.
    *   Or, I can keep it persistent.
    *   Given "Track retry count for each job", persistent seems more appropriate for "tracking".
    *   But if I retry a job manually, I might want to reset.
    *   I'll reset to 0 in `add_job` and increment in `process_job`.
    *   I won't reset in `process_job` unless I clear the job.
    *   Actually, to avoid state leakage between `process_job` calls (e.g. if I call it twice), I should probably reset `retry_counts` to 0 at the start of `process_job`.
    *   But then "Track retry count for each job" is just for that run.
    *   Let's assume `process_job` runs the full logic.
    *   I'll reset `retry_counts` to 0 at the beginning of `process_job` to ensure it reflects the current run's state.
    *   Wait, if I reset to 0, then `self.retry_counts[job_id]` loses history.
    *   I'll just store the count of retries *in this execution*.
    *   I'll set `retry_count = 0` locally.
    *   But I must update `self.retry_counts[job_id]` to satisfy "Track retry count for each job".
    *   Okay, I will update `self.retry_counts[job_id]` on failure.
    *   I will reset `self.retry_counts[job_id] = 0` in `add_job`.
    *   In `process_job`, I will increment `self.retry_counts[job_id]` on failure.
    *   If success, I will leave it or reset.
    *   To be safe, I'll reset `self.retry_counts[job_id] = 0` at the start of `process_job` if I want it to reflect the current run.
    *   Actually, simplest: Local variable `retry_count`. Update `self.retry_counts` only for tracking.
    *   I'll update `self.retry_counts[job_id]` on every failure.
    *   I'll reset it to 0 in `add_job`.
    *   I will NOT reset it in `process_job` unless I define the job as "new".
    *   But if I call `process_job` again on the same ID, should it continue?
    *   I'll assume `process_job` runs the full logic (including retries) and returns result.
    *   So I should reset `retry_counts` to 0 at the start of `process_job` to ensure the count reflects the current execution.
    *   Wait, if I reset to 0, then `self.retry_counts` is just temporary.
    *   Okay, I'll reset `self.retry_counts[job_id] = 0` at start of `process_job`.
    *   And increment on failure.

    *   Wait, if I reset to 0, I should also clear previous state?
    *   Okay, I'll just do `self.retry_counts[job_id] = 0` at start of `process_job`.
    *   And increment `self.retry_counts[job_id]` on failure.
    *   This ensures `self.retry_counts` reflects the retries of the last run.

    *   Wait, `add_job` should also reset `retry_counts`.
    *   Okay.

    *   Wait, I need to make sure I don't import unnecessary things.
    *   `import time` is needed.
    *   `from typing import Callable` is good practice.

    *   Final check on code.
    *   `self.jobs` stores data.
    *   `self.retry_counts` stores count.
    *   `self.backoff` stores delays.
    *   `process_job` loops.
    *   `add_job` stores data.

    *   One edge case: `job_id` not in `self.jobs`.
    *   Return `False`.

    *   One detail: "The backoff delays can be stored/tracked rather than actually sleeping."
    *   I am using `time.sleep(delay)`.
    *   "can be stored/tracked rather than actually sleeping" implies I can just store the delay value used.
    *   But `time.sleep` simulates it.
    *   I'll use `time.sleep` as it's standard for "simulated" waiting in code examples.
    *   Wait, "The backoff delays can be stored/tracked rather than actually sleeping."
    *   Maybe I should store the delay in the job metadata?
    *   I'll just use `time.sleep`. It's valid Python code.
    *   Or I can just record `self.last_backoff[job_id] = delay`.
    *   I'll stick to `time.sleep` for simplicity unless specified otherwise.
    *   Wait, "backoff delays can be stored/tracked rather than actually sleeping" is a hint that I don't *have* to sleep, I can just track the intended delay.
    *   But `time.sleep` is easier to read.
    *   I will use `time.sleep` but also update `self.retry_counts` which tracks the attempt count.
    *   I'll add a `self.backoff_history` or similar?
    *   No, just `self.retry_counts` is enough for "Track retry count".
    *   I'll use `time.sleep`.

    *   Wait, I need to check if I need to import `Callable` from `typing`.
    *   The prompt example uses `queue = JobQueue()`.
    *   I will write the full class.

    *   Wait, one more thing. "Retry up to 3 times".
    *   If I fail 3 times, I stop.
    *   My code allows 4 attempts (0, 1, 2, 3).
    *   Attempt 0: Initial.
    *   Attempt 1: Retry 1.
    *   Attempt 2: Retry 2.
    *   Attempt 3: Retry 3.
    *   This is 3 retries.
    *   My code `while retries <= max_retries` where `max_retries = 3`.
    *   Starts `retries = 0`.
    *   Fail -> `retries = 1`. Sleep.
    *   Fail -> `retries = 2`. Sleep.
    *   Fail -> `retries = 3`. Sleep.
    *   Fail -> `retries = 4`.
    *   Loop `4 <= 3` -> False.
    *   Return False.
    *   This is 3 retries.
    *   Wait, if `retries` starts at 0.
    *   First iteration (Attempt 0).
    *   Fail. `retries` becomes 1.
    *   Sleep 1.
    *   Second iteration (Attempt 1).
    *   Fail. `retries` becomes 2.
    *   Sleep 2.
    *   Third iteration (Attempt 2).
    *   Fail. `retries` becomes 3.
    *   Sleep 4.
    *   Fourth iteration (Attempt 3).
    *   Fail. `retries` becomes 4.
    *   Loop ends.
    *   Return False.
    *   This is 3 retries (1, 2, 3).
    *   Correct.

    *   Wait, `self.retry_counts[job_id] = retries`.
    *   If I return True, `retries` is the count of retries *used* (0, 1, 2, or 3).
    *   If I return False, `retries` is 4 (or 3 if loop ended).
    *   Wait, in my code:
        ```python
        except Exception:
            retries += 1
            self.retry_counts[job_id] = retries
            if retries <= max_retries:
                time.sleep(...)
            else:
                return False
        ```
    *   If `retries` becomes 4.
    *   `4 <= 3` -> False.
    *   `else: return False`.
    *   So `self.retry_counts` is 4.
    *   This is fine.

    *   Wait, I should ensure `process_job` returns `False` if job not found.
    *   I'll add that check.

    *   Okay, ready to write.

</reasoning_content>

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

class JobQueue:
    def __init__(self):
        self.jobs: Dict[str, Any] = {}
        self.retry_counts: Dict[str, int] = {}
        self.backoff_delays = [1, 2, 4]  # 1s, 2s, 4s
        self.max_retries = 3

    def add_job(self, job_id: str, data: 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
        
        retries = 0
        while retries <= self.max_retries:
            try:
                processor(self.jobs[job_id])
                self.retry_counts[job_id] = retries
                return True
            except Exception:
                retries += 1
                self.retry_counts[job_id] = retries
                
                if retries <= self.max_retries:
                    delay = self.backoff_delays[retries - 1]
                    time.sleep(delay)
                else:
                    return False
        
        return False
```
</content>