Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
22 changes: 22 additions & 0 deletions tests/test_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
39 changes: 20 additions & 19 deletions tools/scripts/demo_agent_consumption.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand All @@ -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")
Expand All @@ -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", {})
Expand All @@ -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}")

Expand All @@ -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)")
Expand All @@ -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}")

Expand All @@ -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

Expand Down
Loading