## Plan Modify **one file** only: `devplacepy/services/jobs/deepsearch/crawl.py`. Two functions need the same structural fix – replace manual context‑manager calls with `async with`. ### 1. `_render_with_playwright` (around lines 178–197) **Current pattern (simplified):** ```python pw = await async_playwright().__aenter__() try: browser = await pw.chromium.launch(...) page = await browser.new_page() ... finally: if browser: await browser.close() await pw.__aexit__(None, None, None) ``` **Replace with:** ```python try: async with async_playwright() as pw: try: browser = await pw.chromium.launch(...) page = await browser.new_page() ... finally: if browser: await browser.close() except Exception as exc: logger.warning("playwright render failed: %s", exc) # … existing fallback ``` - Remove the manual `__aenter__()`/`__aexit__()` calls. - Keep the inner `try/finally` for `browser.close()`. - The outer `try/except` remains to catch launch/render errors exactly as before. ### 2. `crawl()` (around lines 303–309, 396–400) **Current pattern:** ```python pw = await async_playwright().__aenter__() browser = await pw.chromium.launch(...) # … finally: if browser: await browser.close() if pw: await pw.__aexit__(None, None, None) ``` **Replace with:** ```python try: async with async_playwright() as pw: browser = await pw.chromium.launch(...) # … crawl logic finally: if browser: await browser.close() ``` - Remove the manual `__aexit__()` call. - `async with` ensures the context manager is properly exited even if an exception occurs. - The `finally` block only needs to close `browser`. **No other files** need modification. The `isslop/browser.py` code is already correct and is left untouched. --- ## Definition of Done - [ ] The file `devplacepy/services/jobs/deepsearch/crawl.py` has been edited per the plan above. No other files are changed. - [ ] The code passes a syntax check: `python -m py_compile devplacepy/services/jobs/deepsearch/crawl.py` exits 0. - [ ] The project’s own verification command runs successfully **without introducing new test failures**: ``` pip install -e '.[dev]' -q && python -m pytest ``` *Pre‑existing failures (e.g., e2e tests that require a Playwright browser binary not present in the test environment) are acceptable as long as they are identical to the base branch failures. The change itself must not cause any new failure.* - [ ] The specific unit test `test_answer_is_grounded_and_cited` (in `tests/unit/services/deepsearch/chat.py`) passes – this test was previously failing only due to merge‑conflict contamination, not to the bug itself. - [ ] (Optional, if environment supports it) A manual DeepSearch query that triggers Playwright fallback (e.g., a JS‑heavy page) completes without `'Playwright' object has no attribute '__aexit__'` error.