From 94b09975f584e335fa1c09dc0e52cdd93ecbfac6 Mon Sep 17 00:00:00 2001 From: Mickael Farina Date: Thu, 2 Jul 2026 20:01:09 +0200 Subject: [PATCH 1/3] fix(pilot): normalize bare-domain URLs in the runner's /navigate (F8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API callers sending {"url":"example.com"} got a 500 from the browser driver (live-verified 2026-07-02); only the UI normalized. The runner now prepends https:// to scheme-less URLs; explicit schemes, about:/ data:/chrome: and scheme-relative URLs pass through untouched. NOTE: pilot-runner (:8094, protected) has NOT been restarted — it keeps running the old code until the operator approves a restart or the next reboot picks this up. Co-Authored-By: Claude Fable 5 --- pilot/pilot_runner.py | 16 ++++++++++++++++ tests/test_pilot_proxy.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/pilot/pilot_runner.py b/pilot/pilot_runner.py index bdfd667..2c6ed61 100644 --- a/pilot/pilot_runner.py +++ b/pilot/pilot_runner.py @@ -296,9 +296,25 @@ def _record_step(action: dict, snap_text: str, el=None, result: str = "") -> Non _recording.steps.append(step) +def _normalize_url(url: str) -> str: + """Prepend https:// when the caller sends a bare domain (F8, 2026-07-03). + + The Pilot UI normalizes before sending, but API callers (pilot skill, + MCP, curl) hitting /navigate with "example.com" got a 500 from the + browser driver. Scheme-relative (//host) and explicit schemes pass + through untouched; about:/data: etc. are left alone.""" + u = (url or "").strip() + if not u: + return u + if "://" in u or u.startswith(("about:", "data:", "chrome:", "//")): + return u + return "https://" + u + + @app.post("/navigate") async def navigate(req: NavigateRequest): pilot = _require_pilot() + req.url = _normalize_url(req.url) await pilot.navigate(req.url, wait_until=req.wait_until) snap = await take_snapshot(pilot.page) _record_step( diff --git a/tests/test_pilot_proxy.py b/tests/test_pilot_proxy.py index 24839f9..06df585 100644 --- a/tests/test_pilot_proxy.py +++ b/tests/test_pilot_proxy.py @@ -39,3 +39,20 @@ def test_headers_always_include_token(tmp_path, monkeypatch): def test_stream_paths_cover_mjpeg(): assert "screenshot/stream" in pilot_proxy._STREAM_PATHS + + +# ── F8 (2026-07): runner-side bare-domain normalization ───────────────────── + + +def test_normalize_url_bare_domain(): + from pilot.pilot_runner import _normalize_url + assert _normalize_url("example.com") == "https://example.com" + assert _normalize_url(" avadigital.ai ") == "https://avadigital.ai" + + +def test_normalize_url_leaves_schemes_alone(): + from pilot.pilot_runner import _normalize_url + for u in ("https://example.com", "http://x.dev", "about:blank", + "data:text/html,hi", "//cdn.example.com/x"): + assert _normalize_url(u) == u + assert _normalize_url("") == "" From 40cfd8bb14cda6fea23f5cf3f8dd70d5efc57bf6 Mon Sep 17 00:00:00 2001 From: Mickael Farina Date: Thu, 2 Jul 2026 20:01:55 +0200 Subject: [PATCH 2/3] docs: resolve pilot-e2e known issue (trace recordings archived out of skills dir) Co-Authored-By: Claude Fable 5 --- docs/known-issues.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/known-issues.md b/docs/known-issues.md index e9303bd..faffde3 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -179,3 +179,8 @@ lack the required `SKILL_NAME` / `SKILL_TRIGGERS` module attrs, so 4 confirmed 2026-07-02 during the log-review PRs). They look like leftover Pilot e2e scratch files, not real skills. Fix: either add the required metadata + regenerate `skills/.manifest.json`, or delete both files. + +**RESOLVED 2026-07-03:** they were auto-generated Pilot trace recordings +(May 13) living in `~/.codec/skills/`, not repo files. Moved to +`~/.codec/pilot_archive/` — recordings preserved, skills dir clean, +`tests/test_skills.py` fully green (160/160). From 0e33a16c82f09d98b0c8c08abc3c62d13f603e11 Mon Sep 17 00:00:00 2001 From: Mickael Farina Date: Thu, 2 Jul 2026 20:25:20 +0200 Subject: [PATCH 3/3] =?UTF-8?q?test:=20importorskip=20playwright=20?= =?UTF-8?q?=E2=80=94=20pilot=20deps=20absent=20on=20CI=20runner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- tests/test_pilot_proxy.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_pilot_proxy.py b/tests/test_pilot_proxy.py index 06df585..ae9f595 100644 --- a/tests/test_pilot_proxy.py +++ b/tests/test_pilot_proxy.py @@ -45,12 +45,16 @@ def test_stream_paths_cover_mjpeg(): def test_normalize_url_bare_domain(): + import pytest + pytest.importorskip("playwright") # pilot deps absent on the CI runner from pilot.pilot_runner import _normalize_url assert _normalize_url("example.com") == "https://example.com" assert _normalize_url(" avadigital.ai ") == "https://avadigital.ai" def test_normalize_url_leaves_schemes_alone(): + import pytest + pytest.importorskip("playwright") # pilot deps absent on the CI runner from pilot.pilot_runner import _normalize_url for u in ("https://example.com", "http://x.dev", "about:blank", "data:text/html,hi", "//cdn.example.com/x"):