fix(vault): stop destroying a second edit while the first is pending#542
fix(vault): stop destroying a second edit while the first is pending#542michiot05 wants to merge 2 commits into
Conversation
the pending-proposal dedup in vault_to_kb matched on page id alone —
_has_pending_page_proposal grew a body-aware mode for exactly this case
("allowing a second different vault edit to file a new proposal even
while the first is still pending"), but no call site ever passed body,
so that mode was dead code.
the consequence is silent data loss in bidirectional sync: edit a
mirrored page (proposal filed), edit it again while the first proposal
is pending, and the forward pass skips the new edit as "unchanged";
the backward pass then rewrites the mirror with kb-canonical content.
the second edit is gone — no proposal, no warning, no file — despite
sync_vault's own contract that the forward pass captures user edits
before the backward mirror overwrites them.
pass the edited body through, so duplicate re-runs still coalesce on
the pending proposal while a genuinely different edit files its own.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
WalkthroughVault sync deduplication now matches pending page proposals by page ID and vault-edit content hash. Distinct sequential edits create separate proposals, including frontmatter-only edits, and end-to-end tests verify later edits survive synchronization. ChangesVault sync proposal deduplication
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/vouch/vault_sync.py`:
- Line 365: Update the deduplication check in the page-sync flow around
_has_pending_page_proposal to compare the complete editable payload recorded by
propose_page—title, type, body, claims, entities, sources, and tags—or a
canonical fingerprint containing those fields. Ensure frontmatter-only edits are
not treated as duplicates, and add a regression test covering an unchanged body
with changed editable metadata.
- Around line 357-364: Convert the prose in the comment describing the duplicate
pending-proposal guard to lowercase, including sentence starts and the quoted
error text where applicable, while preserving the existing wording and code
references.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d27fda8a-1825-4bfd-80ee-5e8bcc8740b4
📒 Files selected for processing (3)
CHANGELOG.mdsrc/vouch/vault_sync.pytests/test_vault_sync.py
address the review round: comparing only the body still swallowed a frontmatter-only edit (title, tags, claims — body untouched) while a matching-body proposal was pending, leaving it to the same backward-pass destruction. rather than comparing every editable payload field, match on the vault-edit source id the proposal already cites: put_source is content-addressed, so the id is the sha256 of the whole mirror file — the canonical fingerprint of the complete edit, body and frontmatter alike. current_hash in the sync loop is that same digest, so the guard needs no extra hashing. lowercase the touched comment prose while here.
|
both findings addressed in the new commit:
validation rerun: |
what changed
vault_to_kb's pending-proposal dedup guard now passes the edited body to_has_pending_page_proposal. the helper has carried a body-aware mode since the #219 fixes — its docstring promises "allowing a second different vault edit to file a new proposal even while the first is still pending" — but no call site ever passedbody, so the helper always fell back to page-id-only matching and that documented behavior was dead code.why
the id-only match silently destroys user data in bidirectional sync:
pages_skipped_unchanged(the cli prints it under "unchanged")kb_to_vaultrewrites the mirror file with kb-canonical content and records the new hash in the state fileafter step 4 the second edit exists nowhere — no proposal, no warning, and the vault file is gone too. that breaks
sync_vault's own documented ordering contract ("forward runs first so user edits land as proposals before the backward mirror overwrites them"), and it's exactly the case the helper's body parameter was written for.with the body passed through, both behaviors compose correctly: re-running sync on the same pending edit still coalesces (the existing dedup test stays green), while a genuinely different edit files its own proposal for the reviewer to pick from.
what would break for an existing
.vouch/nothing — no on-disk shape, object model, or method surface changes. the only behavior change is that a second distinct vault edit now files a proposal where it was previously dropped. kbs relying on the buggy skip would just see one more pending proposal in the review queue, which is the review gate working as designed.
test plan
two regression tests in
tests/test_vault_sync.py, both failing on the previous code:test_vault_to_kb_second_different_edit_files_new_proposal— edit A proposed, edit B while A pending → B files its own proposal (was: misclassified as unchanged)test_sync_vault_preserves_second_edit_while_first_pending— full bidirectional flow → edit B must end up in a proposal (was: destroyed by the backward mirror pass)the existing
test_vault_to_kb_deduplicates_pending_proposals(same-edit re-run coalesces) still passes, so the anti-duplicate guard is intact.validation run locally:
Summary by CodeRabbit
Bug Fixes
Tests