fix(compile): drop same-batch duplicate page titles as collisions#443
fix(compile): drop same-batch duplicate page titles as collisions#443nickmopen wants to merge 1 commit into
Conversation
compile_kb's taken_names set was built once before the phase-1 draft loop and never updated as drafts were accepted, so two same-titled (or same-slugifying) drafts in a single LLM response both passed _draft_problem. Filing both as separate PAGE proposals meant approving them in order silently overwrote the first page via the update_page vault-edit path, with no error or diff surfaced to the reviewer. fold each accepted draft's title/slug into taken_names immediately, same as the pre-existing collision guard already does for a draft that collides with an existing page or a pending proposal. mirrors the incremental-fold pattern phase 2 (wikilink resolution) already uses. fixes vouchdev#439
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 |
|
closing — duplicate of #440, which already fixes this (opened earlier from a separate session under this same account). apologies for the noise. |
Fixes #439
What changed
compile_kb()'s phase-1 draft loop builttaken_namesonce beforevalidating any draft and never grew it as drafts were accepted, unlike
phase 2 (wikilink resolution) just below it, which already folds
survivors back in incrementally. So two same-titled (or
same-slugifying) page drafts returned in a single LLM response both
passed
_draft_problem()and got filed as separate PAGE proposalssharing
id = _slugify(title).proposals.approve()deliberately exempts PAGE proposals from thegeneric "no existing artifact" guard to support the vault-edit flow, so
approving the first duplicate proposal creates the page and approving
the second finds it already exists and silently routes through
update_page()— overwriting the first approved page's content withno error, no diff, and no "already exists" signal to the reviewer.
The fix folds each survivor's title and slug into
taken_namesas soonas it's accepted, so a later draft in the same batch colliding with it
is dropped the same way it would be if it collided with a pre-existing
page or a pending proposal.
Why
Two compiled drafts landing on the same topic in one response is a
plausible LLM output (the compile prompt only tells the model to dedupe
against existing pages/pending proposals, not against its own output),
and the collision guard's own comment in
compile.pyshows this was aknown invariant just not enforced for the same-batch case. Silent
overwrite-on-approve with no audit signal is exactly the failure mode
the collision guard exists to prevent.
What I tested
test_collision_within_same_batch_droppedtotests/test_compile.py, mirroring the existingtest_collision_with_existing_page_dropped/test_collision_with_pending_proposal_droppedstyle: two same-titleddrafts in one
compile_kb()call now yield exactly one proposal, andapproving it preserves the first draft's body.
python -m pytest tests/ -q --ignore=tests/embeddings— full suite greenother than pre-existing, environment-only failures unrelated to this
change (Windows path-separator/
os.getuid/symlink-privilegedifferences and a few tests that spin up a real loopback HTTP server,
which hang under this sandbox's network policy rather than failing —
none touch
compile.py,proposals.py, ortests/test_compile.py).python -m mypy src— clean (pre-existingos.getuid/jinja2Windows-only noise aside).
python -m ruff check src tests— clean.CHANGELOG
Added an entry under
## [Unreleased]/### Fixed.