Skip to content

fix(compile): drop same-batch duplicate page titles as collisions#443

Closed
nickmopen wants to merge 1 commit into
vouchdev:testfrom
nickmopen:fix/compile-same-batch-title-collision
Closed

fix(compile): drop same-batch duplicate page titles as collisions#443
nickmopen wants to merge 1 commit into
vouchdev:testfrom
nickmopen:fix/compile-same-batch-title-collision

Conversation

@nickmopen

Copy link
Copy Markdown

Fixes #439

What changed

compile_kb()'s phase-1 draft loop built taken_names once before
validating 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 proposals
sharing id = _slugify(title).

proposals.approve() deliberately exempts PAGE proposals from the
generic "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 with
no error, no diff, and no "already exists" signal to the reviewer.

The fix folds each survivor's title and slug into taken_names as soon
as 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.py shows this was a
known 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

  • Added test_collision_within_same_batch_dropped to
    tests/test_compile.py, mirroring the existing
    test_collision_with_existing_page_dropped /
    test_collision_with_pending_proposal_dropped style: two same-titled
    drafts in one compile_kb() call now yield exactly one proposal, and
    approving it preserves the first draft's body.
  • python -m pytest tests/ -q --ignore=tests/embeddings — full suite green
    other than pre-existing, environment-only failures unrelated to this
    change (Windows path-separator/os.getuid/symlink-privilege
    differences 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, or tests/test_compile.py).
  • python -m mypy src — clean (pre-existing os.getuid/jinja2
    Windows-only noise aside).
  • python -m ruff check src tests — clean.

CHANGELOG

Added an entry under ## [Unreleased] / ### Fixed.

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
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be6eac83-2c74-48b2-9a51-6424363cbaf7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance tests tests and fixtures size: XS less than 50 changed non-doc lines labels Jul 8, 2026
@nickmopen

Copy link
Copy Markdown
Author

closing — duplicate of #440, which already fixes this (opened earlier from a separate session under this same account). apologies for the noise.

@nickmopen nickmopen closed this Jul 8, 2026
@nickmopen nickmopen deleted the fix/compile-same-batch-title-collision branch July 8, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs documentation, specs, examples, and repo guidance size: XS less than 50 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants