From 1baafae74946eda8c027cba318e66754bef52ddb Mon Sep 17 00:00:00 2001 From: champ Date: Mon, 20 Jul 2026 12:16:56 +0800 Subject: [PATCH 1/2] fix: make demo output Windows-safe --- tests/test_demo.py | 22 ++++++++++++++ tools/scripts/demo_agent_consumption.py | 39 +++++++++++++------------ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/tests/test_demo.py b/tests/test_demo.py index 94425e7..99dc142 100644 --- a/tests/test_demo.py +++ b/tests/test_demo.py @@ -3,6 +3,7 @@ Ensures the end-to-end demo (export-agent -> manifest -> structural checks) passes without errors, using synthetic fixtures only. """ +import os import subprocess import sys from pathlib import Path @@ -28,3 +29,24 @@ def test_demo_runs_successfully(tmp_path): assert result.returncode == 0, f"demo failed:\n{result.stderr}" assert "DEMO PASSED" in result.stdout, "demo did not report PASSED" assert "10/10 structural checks passed" in result.stdout + + +def test_demo_runs_with_cp1252_console(tmp_path): + """The demo must remain usable on legacy Windows console encodings.""" + env = os.environ.copy() + env["PYTHONIOENCODING"] = "cp1252" + result = subprocess.run( + [ + sys.executable, + str(DEMO_SCRIPT), + "--bundle-dir", + str(tmp_path / "cs_demo_cp1252"), + ], + capture_output=True, + text=True, + cwd=REPO_ROOT, + timeout=30, + env=env, + ) + assert result.returncode == 0, f"demo failed under cp1252:\n{result.stderr}" + assert "DEMO PASSED" in result.stdout diff --git a/tools/scripts/demo_agent_consumption.py b/tools/scripts/demo_agent_consumption.py index 1c8aed1..bed6e42 100644 --- a/tools/scripts/demo_agent_consumption.py +++ b/tools/scripts/demo_agent_consumption.py @@ -67,10 +67,10 @@ def step(n: int, title: str) -> None: def main() -> int: bundle_dir_arg = sys.argv[sys.argv.index("--bundle-dir") + 1] if "--bundle-dir" in sys.argv else None - print("╔══════════════════════════════════════════════════════════╗") - print("║ CiteSeal Live Demo: export-agent → consumption ║") - print("║ (fully synthetic data — no real social media content) ║") - print("╚══════════════════════════════════════════════════════════╝") + print("=" * 60) + print(" CiteSeal Live Demo: export-agent -> consumption") + print(" (fully synthetic data - no real social media content)") + print("=" * 60) if not FIXTURE_TWEET_DIR.exists(): print(f"ERROR: fixture not found: {FIXTURE_TWEET_DIR}") @@ -97,8 +97,8 @@ def main() -> int: print(f"ERROR: bundle.json not created at {bundle_json}") return 1 - bundle = json.loads(bundle_json.read_text()) - print(f" ✓ bundle.json created ({bundle_json.stat().st_size} bytes)") + bundle = json.loads(bundle_json.read_text(encoding="utf-8")) + print(f" [OK] bundle.json created ({bundle_json.stat().st_size} bytes)") print(f" item_id: {bundle.get('item_id')}") print(f" author: @{bundle.get('author_handle')}") print(f" assets: {len(bundle.get('assets', []))} files") @@ -111,8 +111,10 @@ def main() -> int: "--dry-run", ]) manifest = json.loads(result.stdout) - manifest_out.write_text(json.dumps(manifest, indent=2, ensure_ascii=False)) - print(f" ✓ manifest.json generated ({manifest_out.stat().st_size} bytes)") + manifest_out.write_text( + json.dumps(manifest, indent=2, ensure_ascii=False), encoding="utf-8" + ) + print(f" [OK] manifest.json generated ({manifest_out.stat().st_size} bytes)") print(f" files hashed: {len(manifest.get('files', []))}") print(f" transforms: {len(manifest.get('transforms', []))}") trust = manifest.get("trust_flags", {}) @@ -130,19 +132,19 @@ def main() -> int: for mpath, mhash in manifest_hashes.items(): if mpath.endswith(fname) and bhash == mhash: verified += 1 - print(f" ✓ {fname}: hash matches manifest") + print(f" [OK] {fname}: hash matches manifest") break total_media = len(bundle.get("media", [])) - print(f" → {verified}/{total_media} media files verified against manifest") + print(f" -> {verified}/{total_media} media files verified against manifest") if verified != total_media: - print(" WARNING: not all media hashes matched — check bundle export") + print(" WARNING: not all media hashes matched - check bundle export") # Step 4: Simulate agent consumption (no LLM needed — structural check) step(4, "Simulate agent consumption (structural validation)") checks = [] def check(label: str, cond: bool) -> None: - status = "✓" if cond else "✗" + status = "[OK]" if cond else "[FAIL]" checks.append(cond) print(f" {status} {label}") @@ -159,7 +161,7 @@ def check(label: str, cond: bool) -> None: all((bundle_dir / "media" / m["file"]).exists() for m in bundle.get("media", []))) all_pass = all(checks) - print(f"\n → {sum(checks)}/{len(checks)} structural checks passed") + print(f"\n -> {sum(checks)}/{len(checks)} structural checks passed") # Step 5: Show what an agent prompt would look like step(5, "Agent consumption prompt (for Claude / Hermes / Codex)") @@ -169,10 +171,9 @@ def check(label: str, cond: bool) -> None: 3. The provenance information (when captured, source URL, export tool) 4. Any trust_flags that indicate data quality issues 5. A suggested citation using the citation_label field""" - print(" ┌─ Claude Code CLI example:") - print(f" │ claude --add-dir {bundle_dir} \\") - print(f' │ "{prompt}"') - print(" └─") + print(" Claude Code CLI example:") + print(f" claude --add-dir {bundle_dir} \\") + print(f' "{prompt}"') print(f"\n Bundle directory: {bundle_dir}") print(f" Manifest: {manifest_out}") @@ -186,11 +187,11 @@ def check(label: str, cond: bool) -> None: # Final result print(f"\n{'='*60}") if all_pass: - print(" ✓ DEMO PASSED — agent consumption pipeline is functional") + print(" [PASS] DEMO PASSED - agent consumption pipeline is functional") print(f"{'='*60}") return 0 else: - print(" ✗ DEMO FAILED — some structural checks did not pass") + print(" [FAIL] DEMO FAILED - some structural checks did not pass") print(f"{'='*60}") return 1 From e070dee359e27468ee3625f8d6a3230ecd80fc1b Mon Sep 17 00:00:00 2001 From: champ Date: Mon, 20 Jul 2026 12:17:22 +0800 Subject: [PATCH 2/2] docs: prepare v0.7.1 release notes --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57b026..3d9ac35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v0.7.1] - 2026-07-20 + +Maintenance release restoring cross-platform demo execution and documenting the repository maintenance process. + +### Fixed +- Made the live demo's terminal output ASCII-safe and its JSON file I/O explicitly UTF-8, preventing `UnicodeEncodeError` failures on legacy Windows console encodings. +- Added a `cp1252` subprocess regression test so Windows console compatibility remains covered by the full test suite. + ### Added - ADR (Architecture Decision Records): `docs/adr/0001-agent-bundle-as-agent-consumption-layer.md`, `docs/adr/0002-local-first-boundary.md` - Good first issue template (`.github/ISSUE_TEMPLATE/good-first-issue.md`) and contribution areas table in CONTRIBUTING.md - Three open good first issues (#2 schema reference, #3 sample archive generator, #4 validation error messages) -- Live demo script `tools/scripts/demo_agent_consumption.py`: end-to-end export-agent → manifest → agent consumption pipeline (synthetic fixtures, 219 tests) +- Live demo script `tools/scripts/demo_agent_consumption.py`: end-to-end export-agent → manifest → agent consumption pipeline using synthetic fixtures + +### Changed +- Defined the maintainer workflow, safety gates, and nightly maintenance boundaries in `docs/maintenance-workflow.md`. ## [v0.7.0] - 2026-07-09