fix(bundle): use POSIX separators in manifest paths and tar arcnames#73
Conversation
…ouchdev#72) build_manifest stored Windows-style 'sources\<sha>\meta.yaml' in manifest.json while tarfile members used 'sources/<sha>/meta.yaml', so on Windows every bundle vouch produced was self-invalid: export_check returned ok=false, manifest.counts was always zero, and import_apply was a silent no-op. Normalise via rel.as_posix() in both build_manifest and export(). Add a regression test that asserts the manifest path set equals the tar member set and that no path contains a backslash. Existing Linux/macOS bundles are unchanged (their paths were already POSIX). Windows bundles produced before this fix should be re-exported.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR fixes a critical Windows bug where ChangesCross-platform bundle path normalization
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Resolves conflicts in CHANGELOG.md, tests/test_sessions.py, and auto-merges src/vouch/sessions.py with the recently-merged: - vouchdev#61 (FTS5 indexing of crystallize summary page) — adds index_db.index_page(...) right after store.put_page(...) inside crystallize. Composes cleanly with this PR's strict-derivation _build_summary_body and the summary_page_id audit fix. - vouchdev#62 (test_crystallize_collects_approval_failures) — preserved as a sibling test. - vouchdev#75 (bundle import sha256 verification) — CHANGELOG entry only. - vouchdev#73 (bundle POSIX separators) — CHANGELOG entry only. Both regression tests added by this PR (test_crystallize_summary_page_does_not_leak_agent_controlled_fields and test_crystallize_audit_event_records_summary_page_id) still pass against the merged sessions.crystallize.
Problem
On Windows,
vouch exportwrites bundle paths intomanifest.jsonusing the native
\separator whiletarfilerecords membernames with
/. Every bundle vouch produces on Windows is thereforeself-invalid:
manifest.countsis{claims: 0, pages: 0, sources: 0, ...}forevery KB regardless of content (the per-subdir counter at
src/vouch/bundle.py:90usesf["path"].startswith(f"{sub}/"), which never matchessources\...).vouch export-checkon the bundle vouch just produced returnsok: falsewithfile in bundle but not in manifestfor everytar member and
manifest lists missing filefor every manifestentry.
vouch import-applyis a silent no-op for the same reason: themanifest-paths set lookup at
src/vouch/bundle.py:264is keyedon the
\-flavoured strings, so every tar member is treatedas "not in manifest" and skipped.
Portable bundles are a load-bearing feature (README: "Portable
bundles"), so this corrupts the only mechanism for moving a KB
between repos on any Windows host. The bug is invisible to CI
because
.github/workflows/ci.ymlonly runs onubuntu-latest,where
Pathalready produces POSIX separators.Fix
Normalise paths to POSIX in two places in
src/vouch/bundle.py:@@ build_manifest for rel, abs_path in _iter_export_files(kb_dir): data = abs_path.read_bytes() files.append({ - "path": str(rel), + "path": rel.as_posix(), "size": len(data), "sha256": sha256_hex(data), }) @@ export with tarfile.open(dest, "w:gz") as tar: for rel, abs_path in _iter_export_files(kb_dir): - tar.add(abs_path, arcname=str(rel)) + tar.add(abs_path, arcname=rel.as_posix())POSIX separators are the on-the-wire format anyway: tarfile uses
/,_unsafe_name_reasonalready checksstartswith("/"), andthe
startswith(f"{sub}/")counter at line 90 assumes the same.Existing Linux/macOS bundles are byte-identical after this change;
Windows bundles become valid. No on-disk-layout, schema, or
bundle-format change — the spec stays as Linux already
emitted it.
Tests
Added
tests/test_bundle.py::test_manifest_paths_match_tar_member_namesas a regression contract: asserts no backslash in any manifest
path, asserts the manifest path set equals the tar member set, and
asserts
manifest["counts"]is non-zero forclaims,pages,and
sources. This guards againststr(rel)slipping back in.Local run on Windows (Python 3.12.13):
ruff check src tests— passes.pytest tests/test_bundle.py— 6/7 pass (the new contracttest is among them;
test_export_import_round_tripnow reachesa separate, pre-existing Windows bug in
put_pageCRLFhandling that is masked on Linux — out of scope for this PR,
reportable separately).
vouch init→vouch source add→vouch export→vouch export-checknow returns{"ok": true, "issues": []}andmanifest.counts.sourcesis2(was0).On Linux/macOS, behaviour and bundle bytes are unchanged.
Fixes #72
Summary by CodeRabbit