# Refactoring Plan: Simplify early cancellation

## Background

The initial implementation of early cancellation used `threading.Event` objects, requiring `_worker` to acquire the shared lock two extra times per check (once to register the event, once to conditionally deregister it) and causing an extra allocation per chunk test. Because the main polling loop in `__main__.py` uses `proc.wait(timeout=0.05)`, it polls via `TimeoutExpired` exceptions and does not actually rely on OS-level thread blocking mechanism that `threading.Event.wait()` provides.

We can achieve the exact same behavior with zero allocation and zero lock-contention overhead by relying on the already-tracked `state.update_counter` and `local_counter`.

---

## Step 1 — Simplify `core.py`

1.  Remove `self.cancel_events` from `_State.__init__`.
2.  Remove the `for ev in state.cancel_events: ev.set()` loop in `_try_commit`.
3.  Update the `test_fn` parameter signature throughout to `Callable[[list[str], Callable[[], bool]], bool]`.
4.  In `_worker`, replace the `threading.Event` registration/deregistration blocks with a simple check closure:
    ```python
    is_cancelled = lambda: state.update_counter != local_counter
    t0 = time.monotonic()
    passed = test_fn(candidate, is_cancelled)
    elapsed = time.monotonic() - t0
    ```

---

## Step 2 — Update `__main__.py` Polling Loop

Update `_make_test_fn` to accept `is_cancelled: Callable[[], bool]` in its return closure instead of `cancel_event: threading.Event`.

Replace:
```python
if cancel_event.is_set():
```
with:
```python
if is_cancelled():
```

---

## Step 3 — Refactor Unit Tests

1.  Update test signatures in `test_core.py` and `test_parallel.py` to expect `is_cancelled` (or `_`).
2.  In `test_cli.py`, replace `cancel_event = threading.Event()` with a mutable mock:
    ```python
    cancelled = False
    is_cancelled = lambda: cancelled
    # ...
    cancelled = True  # instead of cancel_event.set()
    ```
3.  In `test_parallel.py`, rename `test_cancel_events_are_set_on_commit` to `test_early_cancellation_on_commit`. Replace `.wait()` with polling `time.sleep() + is_cancelled()` simulation:
    ```python
    def test_fn(candidate, is_cancelled):
        if essential not in candidate:
            for _ in range(8):  # up to 0.4s
                time.sleep(0.05)
                if is_cancelled():
                    cancel_seen.set()
                    return False
            return False
        return True
    ```
4.  In `test_parallel.py`, update `test_test_fn_called_with_cancel_event` to verify `is_cancelled` is a callable (and maybe call it to ensure it returns a boolean) instead of checking for `isinstance(..., threading.Event)`.

---

## Step 4 — Run Full Test Suite

Run `python -m pytest tests/ -v` to ensure that early cancellation continues to function under this lighter, simpler design without regressions.
