diff --git a/interaction-skills/downloads.md b/interaction-skills/downloads.md
index c1c708c7..01d1f0d1 100644
--- a/interaction-skills/downloads.md
+++ b/interaction-skills/downloads.md
@@ -1,3 +1,96 @@
# Downloads
-Separate browser-triggered downloads from direct `http_get(...)` fetches, and document the minimal signals that prove a download actually started.
+Separate real browser-triggered downloads from native OS save/open dialogs and direct `http_get(...)` fetches.
+
+## What Browser Harness can save directly
+
+Use CDP `Browser.setDownloadBehavior` when clicking a page control causes Chromium to start an actual download (`Content-Disposition: attachment`, ``, blob URL download, app-generated file download, etc.). This avoids the browser's normal download prompts and writes into a known folder.
+
+```python
+import os, time
+
+DL = "/abs/path/to/downloads"
+os.makedirs(DL, exist_ok=True)
+
+cdp("Browser.setDownloadBehavior",
+ behavior="allow",
+ downloadPath=DL,
+ eventsEnabled=True)
+drain_events() # clear stale download/network events
+
+before = set(os.listdir(DL))
+
+# Trigger the site's download: coordinate click, JS click, form submit, etc.
+# click_at_xy(x, y)
+# js("document.querySelector('a.download').click()")
+
+deadline = time.time() + 60
+completed = None
+while time.time() < deadline:
+ for e in drain_events():
+ if e["method"] == "Browser.downloadProgress":
+ p = e["params"]
+ if p.get("state") == "completed":
+ completed = p.get("filePath")
+ break
+ if p.get("state") == "canceled":
+ raise RuntimeError(f"download canceled: {p}")
+ if completed:
+ break
+
+ after = set(os.listdir(DL))
+ new = [f for f in after - before if not f.endswith(".crdownload")]
+ if new and not any(f.endswith(".crdownload") for f in after):
+ completed = os.path.join(DL, sorted(new)[-1])
+ break
+ time.sleep(0.5)
+
+if not completed:
+ raise TimeoutError("download did not complete")
+
+print("downloaded:", completed)
+```
+
+Rename after completion when the task requires a specific filename:
+
+```python
+target = os.path.join(DL, "report_2026-04_short.pdf")
+os.replace(completed, target)
+print(target)
+```
+
+## Signals that prove a download happened
+
+- `Browser.downloadWillBegin` / `Browser.downloadProgress` events after `eventsEnabled=True`.
+- A new file appears in `downloadPath`.
+- No `.crdownload` files remain in the folder.
+- The final file has a plausible extension/size; for PDFs, verify the header or run `pdftotext` when accuracy matters.
+
+## Filename control
+
+CDP can choose the folder reliably. The filename usually comes from the server (`Content-Disposition`), an `` attribute, or Chromium's blob/download naming logic. For deterministic task names, let the download finish, then `os.replace(...)` it to the requested name.
+
+`behavior="allowAndName"` is useful for collision-free bulk downloads, but it names files by GUID. Use it only when you plan to map events and rename every file yourself.
+
+## When to use `http_get(...)` instead
+
+If the file URL is static and does not require browser-only state, skip the browser:
+
+```python
+data = http_get("https://example.com/file.csv")
+open("/abs/path/file.csv", "w").write(data)
+```
+
+For authenticated apps, first check Network/XHR for the actual export request. If it can be replayed with cookies/headers, direct HTTP is faster and less fragile than driving the UI. If the file is generated only by page JS or a blob URL, use browser-triggered download instead.
+
+## Native dialogs are not browser downloads
+
+macOS/Windows file chooser and save sheets are outside the page DOM and are not JavaScript dialogs. Browser Harness cannot inspect or fill their fields through `page_info()`, `js(...)`, or normal CDP DOM commands.
+
+Use Computer Use when the site flow opens a native OS sheet, for example:
+
+- `Save As...` / `Save as PDF` from the browser print UI.
+- A macOS save sheet asking for filename and folder.
+- An OS file picker not backed by an accessible `` path.
+
+Avoid Computer Use when possible by finding an actual download button, a private export endpoint, or a blob URL that triggers Chromium's download manager. If none exists, Harness can drive the web page up to the native sheet, then Computer Use handles the sheet.