From a87c1ba05832a56a050068bd934ca6c8542924d8 Mon Sep 17 00:00:00 2001 From: Daniel Zivkovic Date: Sat, 2 May 2026 18:41:50 -0400 Subject: [PATCH 1/3] fix(render): use forward slashes in save-path footer for Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The footer line `📎 Raw results saved to ~/Documents\Last30Days\…` mangled the home-relative path on Windows because `f"~/{relative}"` stringifies a `pathlib.Path` with the OS-native separator. The result mixes a Unix tilde with backslashes, which neither File Explorer, PowerShell, nor a `file://` URI can resolve. `Path.as_posix()` always returns forward slashes, which is the convention `~/`-prefixed paths require on every platform. macOS and Linux output is unchanged because their separator is already `/`. Repro on Windows: python3 last30days.py "anything" --emit=compact --save-dir="$HOME/Documents/Last30Days" # before: 📎 Raw results saved to ~/Documents\Last30Days\anything-raw.md # after: 📎 Raw results saved to ~/Documents/Last30Days/anything-raw.md --- skills/last30days/scripts/last30days.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/last30days/scripts/last30days.py b/skills/last30days/scripts/last30days.py index 960cb2d..4efd03b 100644 --- a/skills/last30days/scripts/last30days.py +++ b/skills/last30days/scripts/last30days.py @@ -195,7 +195,7 @@ def compute_save_path_display(save_dir: str, topic: str, suffix: str, emit: str) try: home = _Path.home().resolve() relative = raw.relative_to(home) - return f"~/{relative}" + return f"~/{relative.as_posix()}" except ValueError: return str(raw) From 5817ef83876b909ad44f776c11ee527fda9e696a Mon Sep 17 00:00:00 2001 From: Daniel Zivkovic Date: Sat, 2 May 2026 18:56:29 -0400 Subject: [PATCH 2/3] test(cli): regression test for Windows save-path display Asserts compute_save_path_display() never returns a backslash when the save_dir is under the user's home directory, regardless of host OS. Reproduces the original bug on Windows (failed message before the fix: AssertionError: '\' unexpectedly found in '~/l30d_save_path__luu2g76\Documents\Last30Days\british-airways-middle-east-raw-v3.md' ) and locks in the contract on POSIX hosts (passes trivially today; would fail if anyone removes .as_posix() in the future). Verified by temporarily reverting the fix and confirming RED, then re-applying the fix and confirming GREEN. All 13 CliV3Tests pass. --- tests/test_cli_v3.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_cli_v3.py b/tests/test_cli_v3.py index 48f2dd1..feba03d 100644 --- a/tests/test_cli_v3.py +++ b/tests/test_cli_v3.py @@ -143,6 +143,31 @@ class CliV3Tests(unittest.TestCase): _, kwargs = write_text.call_args self.assertEqual("utf-8", kwargs.get("encoding")) + def test_compute_save_path_display_uses_posix_slashes_under_home(self): + # Regression: f"~/{relative}" stringified pathlib.Path with the + # OS-native separator, producing "~/Documents\\Last30Days\\..." on + # Windows that no shell or File Explorer could open. The fix is + # f"~/{relative.as_posix()}" which forces forward slashes regardless + # of host OS. On POSIX hosts this asserts the contract for + # cross-platform safety; on Windows hosts it would fail without the fix. + import shutil + real_home = Path.home() + tmp_under_home = Path(tempfile.mkdtemp(prefix="l30d_save_path_", dir=str(real_home))) + try: + save_dir = tmp_under_home / "Documents" / "Last30Days" + save_dir.mkdir(parents=True, exist_ok=True) + display = cli.compute_save_path_display( + str(save_dir), "british airways middle east", "v3", "compact" + ) + self.assertTrue(display.startswith("~/"), f"Expected '~/' prefix, got: {display}") + self.assertNotIn("\\", display, f"Backslash leaked into display: {display}") + self.assertTrue( + display.endswith("british-airways-middle-east-raw-v3.md"), + f"Expected slug+suffix at end, got: {display}", + ) + finally: + shutil.rmtree(tmp_under_home, ignore_errors=True) + def test_persist_report_updates_run_status_on_success_and_failure(self): report = self.make_report() From 0e353ae03fb98d7aa4165b8b32ba4311f6539b30 Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Sun, 17 May 2026 00:19:49 -0700 Subject: [PATCH 3/3] fix(render): apply as_posix to fallback branch + hoist shutil import --- skills/last30days/scripts/last30days.py | 2 +- tests/test_cli_v3.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/last30days/scripts/last30days.py b/skills/last30days/scripts/last30days.py index 4efd03b..6a82454 100644 --- a/skills/last30days/scripts/last30days.py +++ b/skills/last30days/scripts/last30days.py @@ -197,7 +197,7 @@ def compute_save_path_display(save_dir: str, topic: str, suffix: str, emit: str) relative = raw.relative_to(home) return f"~/{relative.as_posix()}" except ValueError: - return str(raw) + return raw.as_posix() def read_synthesis_file(path: str) -> str: diff --git a/tests/test_cli_v3.py b/tests/test_cli_v3.py index feba03d..23e5dc2 100644 --- a/tests/test_cli_v3.py +++ b/tests/test_cli_v3.py @@ -1,6 +1,7 @@ # ruff: noqa: E402 import json import io +import shutil import tempfile import subprocess import sys @@ -150,7 +151,6 @@ class CliV3Tests(unittest.TestCase): # f"~/{relative.as_posix()}" which forces forward slashes regardless # of host OS. On POSIX hosts this asserts the contract for # cross-platform safety; on Windows hosts it would fail without the fix. - import shutil real_home = Path.home() tmp_under_home = Path(tempfile.mkdtemp(prefix="l30d_save_path_", dir=str(real_home))) try: