Skip to content

fix: prevent same-batch duplicate page proposals in compile_kb (#439)#453

Open
Tet-9 wants to merge 1 commit into
vouchdev:testfrom
Tet-9:fix/compile-same-batch-duplicate
Open

fix: prevent same-batch duplicate page proposals in compile_kb (#439)#453
Tet-9 wants to merge 1 commit into
vouchdev:testfrom
Tet-9:fix/compile-same-batch-duplicate

Conversation

@Tet-9

@Tet-9 Tet-9 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description:

Fixes #439
Problem
compile_kb() seeds taken_names once from on-disk pages and pending proposals, before the phase-1 draft-validation loop runs. It's never updated as drafts are accepted within that same loop. If an LLM batch drafts two pages with the same title — or titles that slugify to the same id (e.g. "Retry Policy" vs "retry policy") — both pass _draft_problem's collision guard and get filed as separate PENDING page proposals with the same underlying id.
Approving the first creates the page. Approving the second silently routes through update_page() (the vault-edit path) and overwrites the first — no error, no diff surfaced to the reviewer.

Fix

Fold each survivor's title and slug into taken_names immediately after it's accepted in the phase-1 loop, mirroring what phase 2 already does for wikilink resolution. A later draft in the same batch with a colliding title/slug now correctly fails the collision guard and is dropped with reason "page for {title!r} already exists or is pending review".

Testing

Added test_same_batch_duplicate_title_drops_second — exact-title collision within one batch
Added test_same_batch_duplicate_slug_drops_second — same-slug, different-casing collision
Both fail without the fix, pass with it
Full suite: make check clean (ruff, mypy), pytest 1160/1161 passing — the one failure (test_git_changes_in_a_real_repo) is a pre-existing local GPG-agent environment issue, confirmed unrelated by reproducing it identically with these changes stashed
CHANGELOG.md updated under [Unreleased]

…dev#439)

taken_names was only seeded from on-disk pages and pending proposals,
never updated as drafts were accepted within the phase-1 loop. Two
same-titled (or same-slug) drafts from one LLM batch both passed the
collision guard, filing duplicate PAGE proposals. Approving the second
would silently route through update_page() and overwrite the first.

Fold each survivor's title/slug into taken_names immediately upon
acceptance, mirroring what phase 2 already does for wikilinks.
@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance ci github actions and automation cli command line interface mcp mcp, jsonl, and http surfaces storage kb storage, migrations, schemas, and proposals retrieval context, search, synthesis, and evaluation embeddings embedding-backed retrieval tests tests and fixtures size: XL 1000 or more changed non-doc lines labels Jul 10, 2026
@Tet-9 Tet-9 changed the base branch from main to test July 10, 2026 09:21
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds dependency auditing, hardens bundle import/export and durable storage, expands retrieval, health checks, session handling, MCP/JSONL APIs, and embedding tests. It also adds Vouch records and removes the desktop application and prior specification artifacts.

Changes

Core platform changes

Layer / File(s) Summary
CI and project documentation
.github/workflows/security-audit.yml, CHANGELOG.md, CONTRIBUTING.md, README.md, docs/superpowers/specs/*
Adds a scheduled non-blocking pip-audit workflow and updates project documentation, contribution requirements, changelog, and Review UI design notes.
Bundle and storage integrity
src/vouch/bundle.py, src/vouch/storage.py
Adds manifest/path/hash validation, graph and source integrity checks, safe file access, resilient loading, reference validation, UTF-8 persistence, and update methods.
Retrieval, indexes, and health
src/vouch/context.py, src/vouch/index_db.py, src/vouch/health.py, src/vouch/embeddings/base.py
Adds configurable retrieval backends, hybrid search, graph expansion, deduplication, embedding/provenance caches, fsck, index drift checks, and progress-aware rebuilds.
MCP and JSONL APIs
src/vouch/server.py, src/vouch/jsonl_server.py
Adds scoped search/context, synthesis, diffing, triage, lifecycle, compile, embedding, provenance, graph, theme, and volunteer-context operations.
Session handling and validation
src/vouch/sessions.py, tests/embeddings/*
Connects session lifecycle hooks and summary-page crystallization to salience, volunteer context, storage, and indexing, with expanded embedding integration and endpoint tests.
Vouch artifacts and captures
.vouch/*
Adds claims, pages, sources, decisions, schema metadata, and captured command histories.
Removed desktop and specification content
desktop/*, spec/*, migrations/README.md, .pre-commit-config.yaml
Removes the desktop Electron application, its tests/configuration, the specification documents, migration README, and configured pre-commit hooks.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant JSONLServer
  participant ContextBuilder
  participant IndexDB
  Client->>JSONLServer: kb.search or kb.context
  JSONLServer->>ContextBuilder: build scoped retrieval request
  ContextBuilder->>IndexDB: query embeddings and FTS indexes
  IndexDB-->>ContextBuilder: ranked and enriched hits
  ContextBuilder-->>JSONLServer: context or search envelope
  JSONLServer-->>Client: backend, viewer, hits, and optional explain data
Loading

Possibly related PRs

Suggested labels: ci

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes many unrelated desktop, spec, docs, and vouch capture changes beyond the compile_kb collision fix. Split the unrelated desktop/spec/docs/capture changes into separate PRs and keep this change focused on the compile_kb fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The compile_kb fix updates taken_names as drafts are accepted, which blocks same-batch title/slug collisions as required by #439.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the core fix in compile_kb: preventing same-batch duplicate page proposals for issue #439.
✨ 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 size: S 50-199 changed non-doc lines and removed ci github actions and automation cli command line interface mcp mcp, jsonl, and http surfaces storage kb storage, migrations, schemas, and proposals retrieval context, search, synthesis, and evaluation embeddings embedding-backed retrieval size: XL 1000 or more changed non-doc lines labels Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl (1)

1-94: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not commit raw capture buffers.

.vouch/captures/ is documented as local-only and is deleted after finalization, but this tracked file exposes absolute paths, usernames, repository activity, and credential-store details (notably the command referencing /home/a/.config/mcp-publisher/token.json). Remove it from the commit; if already published, purge it from repository history.

🤖 Prompt for 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.

In @.vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl around lines 1 -
94, Remove the tracked capture buffer under .vouch/captures from the commit and
ensure it is excluded by the repository’s ignore rules. If it exists in
published history, purge the file from all repository history and force-push the
cleaned history as required; verify no credential-store references or other
sensitive capture data remain tracked.
🧹 Nitpick comments (1)
src/vouch/sessions.py (1)

37-38: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Isolate the volunteer hook from session start. session_start() has already persisted and audit-logged the session before calling volunteer_context.on_session_start(), so an exception in hot_memory.register() or _start_watch() aborts kb_session_start() before register_mcp_push() runs. Extend the existing best-effort handling to the whole hook so session creation doesn’t fail on volunteer setup.

🤖 Prompt for 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.

In `@src/vouch/sessions.py` around lines 37 - 38, The volunteer hook currently
propagates setup exceptions after the session is persisted, preventing
subsequent session initialization. In session_start(), wrap the entire
volunteer_context.on_session_start(store, sess) call in the existing best-effort
exception handling, including hot_memory.register() and _start_watch() failures,
so exceptions are logged or swallowed consistently and the function still
returns sess for register_mcp_push() to run.
🤖 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 @.github/workflows/security-audit.yml:
- Line 27: Update the actions/checkout@v4 step to set persist-credentials:
false, preventing the GITHUB_TOKEN from being stored in local Git configuration
before installation code runs.
- Around line 25-40: Remove the job-level continue-on-error setting so checkout,
Python setup, and dependency installation failures remain fatal. Add
continue-on-error only to the step named “audit” that runs pip-audit, preserving
non-blocking audit findings without masking setup failures.

In @.vouch/decided/20260707-092531-154cb163.yaml:
- Around line 42-45: Replace the non-actionable decision_reason value “yes” with
a concise, specific explanation of why the proposal was rejected, preserving the
existing status and decision metadata.

In
@.vouch/pages/session-what-are-the-non-development-related-files-and-folde.md:
- Around line 36-38: Replace the host-specific absolute paths in the session
page with repository-relative or otherwise redacted paths, removing usernames,
temporary directories, and private memory or scratchpad filenames. Also update
the corresponding decided record containing the same capture data to apply the
same redaction.
- Line 42: Normalize the captured Markdown in the affected fenced blocks: add a
language identifier to each fence to satisfy MD040, and remove invalid leading
or trailing spaces from the command inline code spans to satisfy MD038. Apply
the same cleanup to both referenced occurrences.

In `@CONTRIBUTING.md`:
- Around line 128-130: The contribution guidelines should distinguish evidence
requirements by change type: require vouch-ui screenshots or screen recording
for web app changes, and terminal captures for CLI-output changes. Update the
wording to use “screen recording” and “web app,” without requiring web-app
evidence for CLI-only changes.

In `@docs/superpowers/specs/2026-07-06-review-ui-multi-kb-design.md`:
- Around line 3-9: The spec metadata includes non-reproducible local filesystem
and unpushed branch details. Update the introductory status text in the
review-ui multi-KB design spec to remove the local path and branch reference,
replacing them with a stable repository or commit link, or a concise
implementation-status statement.

In `@src/vouch/health.py`:
- Around line 576-626: Wire the existing _tick progress helper into
rebuild_index’s stages: call it with "claims", "pages", and "entities" before
their respective indexing loops, and with "embeddings" before
_rebuild_embeddings(store). Preserve the documented phase labels and on_progress
behavior.
- Around line 629-651: Update _rebuild_embeddings to populate the
embedding_index table used by search_semantic() in addition to the legacy
embeddings table written by index_db.index_embedding(). After generating each
vector, invoke the appropriate embedding_index insertion/upsert helper with the
same kind, entity ID, and vector, ensuring rebuilt indexes immediately support
backend=embedding searches.

In `@src/vouch/index_db.py`:
- Around line 134-167: The rebuild and semantic search paths use different
embedding tables, so reindexing leaves embedding search empty. Update the
rebuild logic and its embedding write helper, including index_embedding, to
populate the embedding_index table consumed by search_semantic/search_embedding,
or consolidate both paths onto one shared table and update all related queries
consistently.

In `@src/vouch/jsonl_server.py`:
- Around line 713-726: Update _h_propose_theme to use _agent() as the fallback
when resolving actor, while preserving an explicitly supplied p["agent"] value.
Replace the environment-variable fallback so the HTTP transport’s _actor
ContextVar and X-Vouch-Agent attribution are honored consistently in
themes.propose_theme.

In `@tests/embeddings/test_integration.py`:
- Around line 48-72: Ensure test_semantic_search_finds_lexically_disjoint_claim
registers the mock embedder used by the other embedding tests before calling
index_db.search_semantic, or skip the test with importorskip when the real
embeddings dependency is unavailable. Keep the assertion unchanged once search
is guaranteed to return results.

---

Outside diff comments:
In @.vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl:
- Around line 1-94: Remove the tracked capture buffer under .vouch/captures from
the commit and ensure it is excluded by the repository’s ignore rules. If it
exists in published history, purge the file from all repository history and
force-push the cleaned history as required; verify no credential-store
references or other sensitive capture data remain tracked.

---

Nitpick comments:
In `@src/vouch/sessions.py`:
- Around line 37-38: The volunteer hook currently propagates setup exceptions
after the session is persisted, preventing subsequent session initialization. In
session_start(), wrap the entire volunteer_context.on_session_start(store, sess)
call in the existing best-effort exception handling, including
hot_memory.register() and _start_watch() failures, so exceptions are logged or
swallowed consistently and the function still returns sess for
register_mcp_push() to run.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f56e3d1d-9b90-4f73-a0ad-04b6f3bcacb2

📥 Commits

Reviewing files that changed from the base of the PR and between 5c98a3a and 71db0a8.

⛔ Files ignored due to path filters (6)
  • desktop/docs/screenshots/browse.png is excluded by !**/*.png
  • desktop/docs/screenshots/dashboard.png is excluded by !**/*.png
  • desktop/docs/screenshots/dual-solve.png is excluded by !**/*.png
  • desktop/docs/screenshots/review.png is excluded by !**/*.png
  • desktop/package-lock.json is excluded by !**/package-lock.json
  • docs/banner.svg is excluded by !**/*.svg
📒 Files selected for processing (123)
  • .github/workflows/security-audit.yml
  • .pre-commit-config.yaml
  • .vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl
  • .vouch/captures/5720220c-b77c-4947-aeec-9d6ef91d70b8.jsonl
  • .vouch/captures/8a6df051-6c56-4d86-b0ed-66f6638ecfdc.jsonl
  • .vouch/captures/ec4b8842-f167-467e-a33c-dc87911c9e0e.jsonl
  • .vouch/claims/vouch-starter-reviewed-knowledge.yaml
  • .vouch/decided/20260707-092531-154cb163.yaml
  • .vouch/decided/20260707-093005-05345f47.yaml
  • .vouch/decided/20260707-093125-4d6972f9.yaml
  • .vouch/pages/edit-in-obsidian.md
  • .vouch/pages/reviewed-knowledge-store.md
  • .vouch/pages/session-what-are-the-non-development-related-files-and-folde.md
  • .vouch/schema_version
  • .vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/content
  • .vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/meta.yaml
  • CHANGELOG.md
  • CONTRIBUTING.md
  • README.md
  • desktop/.gitignore
  • desktop/CHANGELOG.md
  • desktop/LICENSE
  • desktop/README.md
  • desktop/docs/architecture.md
  • desktop/docs/reports/2026-06-26.md
  • desktop/electron-builder.yml
  • desktop/electron.vite.config.ts
  • desktop/package.json
  • desktop/resources/vouch/.gitkeep
  • desktop/scripts/dev-with-vouch.ts
  • desktop/scripts/gen-methods.ts
  • desktop/src/catalog/backend.json
  • desktop/src/catalog/methods.json
  • desktop/src/main/http-client.ts
  • desktop/src/main/index.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/jsonl-client.ts
  • desktop/src/main/kb-store.ts
  • desktop/src/main/supervisor.ts
  • desktop/src/main/tray.ts
  • desktop/src/main/types.ts
  • desktop/src/main/vouch-locator.ts
  • desktop/src/preload/index.ts
  • desktop/src/renderer/index.html
  • desktop/src/renderer/src/App.tsx
  • desktop/src/renderer/src/app.css
  • desktop/src/renderer/src/components/Diff.tsx
  • desktop/src/renderer/src/components/Drawer.tsx
  • desktop/src/renderer/src/components/EmptyState.tsx
  • desktop/src/renderer/src/components/MethodCard.tsx
  • desktop/src/renderer/src/components/MethodForm.tsx
  • desktop/src/renderer/src/components/Placeholder.tsx
  • desktop/src/renderer/src/components/Rail.tsx
  • desktop/src/renderer/src/components/StatusBar.tsx
  • desktop/src/renderer/src/components/Topbar.tsx
  • desktop/src/renderer/src/components/controls/index.tsx
  • desktop/src/renderer/src/components/results/Cards.tsx
  • desktop/src/renderer/src/components/results/JsonTree.tsx
  • desktop/src/renderer/src/components/results/Renderers.tsx
  • desktop/src/renderer/src/components/results/ResultView.tsx
  • desktop/src/renderer/src/components/results/atoms.tsx
  • desktop/src/renderer/src/global.d.ts
  • desktop/src/renderer/src/lib/VouchContext.tsx
  • desktop/src/renderer/src/lib/client.ts
  • desktop/src/renderer/src/lib/format.ts
  • desktop/src/renderer/src/lib/useOnOpen.tsx
  • desktop/src/renderer/src/lib/useVouchEvents.ts
  • desktop/src/renderer/src/main.tsx
  • desktop/src/renderer/src/views/Dashboard.tsx
  • desktop/src/renderer/src/views/DualSolve.tsx
  • desktop/src/renderer/src/views/GenericView.tsx
  • desktop/src/renderer/src/views/Review.tsx
  • desktop/src/renderer/src/views/blurbs.ts
  • desktop/src/renderer/src/views/registry.tsx
  • desktop/src/shared/ipc.ts
  • desktop/src/shared/methods.gen.ts
  • desktop/src/shared/methods.types.ts
  • desktop/test/catalog.test.ts
  • desktop/test/controls.test.tsx
  • desktop/test/gen-methods.test.ts
  • desktop/test/http-client.test.ts
  • desktop/test/jsonl-client.test.ts
  • desktop/test/method-card.test.tsx
  • desktop/test/method-form.test.tsx
  • desktop/test/method-gate.test.ts
  • desktop/test/smoke-jsonl.ts
  • desktop/test/vouch-locator.test.ts
  • desktop/tsconfig.json
  • desktop/tsconfig.node.json
  • desktop/tsconfig.scripts.json
  • desktop/tsconfig.web.json
  • desktop/vitest.config.ts
  • docs/superpowers/specs/2026-07-06-review-ui-multi-kb-design.md
  • migrations/README.md
  • spec/2026-05-21/README.md
  • spec/2026-05-21/SPEC.md
  • spec/2026-05-21/audit-vocabulary.md
  • spec/2026-05-21/methods.md
  • spec/2026-05-21/retrieval.md
  • spec/2026-05-21/review-gate.md
  • spec/2026-05-21/transports.md
  • spec/README.md
  • spec/audit-vocabulary.md
  • spec/methods.md
  • spec/retrieval.md
  • spec/review-gate.md
  • spec/transports.md
  • src/vouch/bundle.py
  • src/vouch/cli.py
  • src/vouch/compile.py
  • src/vouch/context.py
  • src/vouch/embeddings/base.py
  • src/vouch/health.py
  • src/vouch/index_db.py
  • src/vouch/jsonl_server.py
  • src/vouch/server.py
  • src/vouch/sessions.py
  • src/vouch/storage.py
  • tests/embeddings/conftest.py
  • tests/embeddings/test_integration.py
  • tests/embeddings/test_search.py
  • tests/test_cli.py
  • tests/test_compile.py
💤 Files with no reviewable changes (87)
  • spec/methods.md
  • desktop/.gitignore
  • desktop/electron-builder.yml
  • .pre-commit-config.yaml
  • desktop/test/gen-methods.test.ts
  • desktop/test/http-client.test.ts
  • desktop/CHANGELOG.md
  • desktop/README.md
  • desktop/test/method-gate.test.ts
  • desktop/docs/architecture.md
  • desktop/src/main/kb-store.ts
  • migrations/README.md
  • desktop/tsconfig.scripts.json
  • desktop/electron.vite.config.ts
  • desktop/src/renderer/src/components/Topbar.tsx
  • desktop/src/renderer/index.html
  • desktop/src/main/supervisor.ts
  • desktop/src/renderer/src/components/results/JsonTree.tsx
  • desktop/package.json
  • spec/2026-05-21/README.md
  • spec/README.md
  • desktop/src/renderer/src/app.css
  • desktop/src/renderer/src/views/Dashboard.tsx
  • spec/transports.md
  • desktop/LICENSE
  • desktop/src/renderer/src/lib/format.ts
  • desktop/src/renderer/src/components/Drawer.tsx
  • desktop/src/renderer/src/components/Rail.tsx
  • desktop/docs/reports/2026-06-26.md
  • desktop/scripts/gen-methods.ts
  • desktop/test/catalog.test.ts
  • desktop/src/renderer/src/views/registry.tsx
  • spec/2026-05-21/transports.md
  • desktop/src/main/ipc.ts
  • desktop/src/renderer/src/components/Placeholder.tsx
  • desktop/src/renderer/src/global.d.ts
  • desktop/src/renderer/src/views/DualSolve.tsx
  • spec/2026-05-21/audit-vocabulary.md
  • desktop/src/renderer/src/components/Diff.tsx
  • desktop/test/jsonl-client.test.ts
  • spec/retrieval.md
  • spec/2026-05-21/methods.md
  • desktop/src/main/index.ts
  • desktop/src/shared/methods.types.ts
  • desktop/src/renderer/src/main.tsx
  • desktop/vitest.config.ts
  • desktop/src/renderer/src/components/MethodForm.tsx
  • desktop/test/controls.test.tsx
  • spec/review-gate.md
  • desktop/tsconfig.node.json
  • desktop/tsconfig.json
  • spec/audit-vocabulary.md
  • desktop/src/renderer/src/components/EmptyState.tsx
  • desktop/src/renderer/src/views/blurbs.ts
  • desktop/src/catalog/methods.json
  • spec/2026-05-21/review-gate.md
  • desktop/src/shared/methods.gen.ts
  • desktop/src/main/http-client.ts
  • desktop/src/renderer/src/components/results/ResultView.tsx
  • spec/2026-05-21/SPEC.md
  • desktop/src/main/vouch-locator.ts
  • desktop/src/main/tray.ts
  • desktop/src/main/types.ts
  • desktop/src/renderer/src/views/GenericView.tsx
  • desktop/src/renderer/src/components/MethodCard.tsx
  • desktop/src/renderer/src/components/StatusBar.tsx
  • desktop/src/renderer/src/views/Review.tsx
  • desktop/src/shared/ipc.ts
  • desktop/src/renderer/src/components/results/atoms.tsx
  • desktop/src/renderer/src/lib/useOnOpen.tsx
  • desktop/scripts/dev-with-vouch.ts
  • desktop/src/catalog/backend.json
  • desktop/src/renderer/src/App.tsx
  • desktop/test/method-card.test.tsx
  • desktop/src/main/jsonl-client.ts
  • desktop/src/renderer/src/components/controls/index.tsx
  • desktop/test/method-form.test.tsx
  • desktop/test/smoke-jsonl.ts
  • desktop/tsconfig.web.json
  • desktop/src/renderer/src/lib/client.ts
  • desktop/test/vouch-locator.test.ts
  • desktop/src/preload/index.ts
  • spec/2026-05-21/retrieval.md
  • desktop/src/renderer/src/lib/VouchContext.tsx
  • desktop/src/renderer/src/components/results/Cards.tsx
  • desktop/src/renderer/src/lib/useVouchEvents.ts
  • desktop/src/renderer/src/components/results/Renderers.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl (1)

1-94: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not commit raw capture buffers.

.vouch/captures/ is documented as local-only and is deleted after finalization, but this tracked file exposes absolute paths, usernames, repository activity, and credential-store details (notably the command referencing /home/a/.config/mcp-publisher/token.json). Remove it from the commit; if already published, purge it from repository history.

🤖 Prompt for 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.

In @.vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl around lines 1 -
94, Remove the tracked capture buffer under .vouch/captures from the commit and
ensure it is excluded by the repository’s ignore rules. If it exists in
published history, purge the file from all repository history and force-push the
cleaned history as required; verify no credential-store references or other
sensitive capture data remain tracked.
🧹 Nitpick comments (1)
src/vouch/sessions.py (1)

37-38: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Isolate the volunteer hook from session start. session_start() has already persisted and audit-logged the session before calling volunteer_context.on_session_start(), so an exception in hot_memory.register() or _start_watch() aborts kb_session_start() before register_mcp_push() runs. Extend the existing best-effort handling to the whole hook so session creation doesn’t fail on volunteer setup.

🤖 Prompt for 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.

In `@src/vouch/sessions.py` around lines 37 - 38, The volunteer hook currently
propagates setup exceptions after the session is persisted, preventing
subsequent session initialization. In session_start(), wrap the entire
volunteer_context.on_session_start(store, sess) call in the existing best-effort
exception handling, including hot_memory.register() and _start_watch() failures,
so exceptions are logged or swallowed consistently and the function still
returns sess for register_mcp_push() to run.
🤖 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 @.github/workflows/security-audit.yml:
- Line 27: Update the actions/checkout@v4 step to set persist-credentials:
false, preventing the GITHUB_TOKEN from being stored in local Git configuration
before installation code runs.
- Around line 25-40: Remove the job-level continue-on-error setting so checkout,
Python setup, and dependency installation failures remain fatal. Add
continue-on-error only to the step named “audit” that runs pip-audit, preserving
non-blocking audit findings without masking setup failures.

In @.vouch/decided/20260707-092531-154cb163.yaml:
- Around line 42-45: Replace the non-actionable decision_reason value “yes” with
a concise, specific explanation of why the proposal was rejected, preserving the
existing status and decision metadata.

In
@.vouch/pages/session-what-are-the-non-development-related-files-and-folde.md:
- Around line 36-38: Replace the host-specific absolute paths in the session
page with repository-relative or otherwise redacted paths, removing usernames,
temporary directories, and private memory or scratchpad filenames. Also update
the corresponding decided record containing the same capture data to apply the
same redaction.
- Line 42: Normalize the captured Markdown in the affected fenced blocks: add a
language identifier to each fence to satisfy MD040, and remove invalid leading
or trailing spaces from the command inline code spans to satisfy MD038. Apply
the same cleanup to both referenced occurrences.

In `@CONTRIBUTING.md`:
- Around line 128-130: The contribution guidelines should distinguish evidence
requirements by change type: require vouch-ui screenshots or screen recording
for web app changes, and terminal captures for CLI-output changes. Update the
wording to use “screen recording” and “web app,” without requiring web-app
evidence for CLI-only changes.

In `@docs/superpowers/specs/2026-07-06-review-ui-multi-kb-design.md`:
- Around line 3-9: The spec metadata includes non-reproducible local filesystem
and unpushed branch details. Update the introductory status text in the
review-ui multi-KB design spec to remove the local path and branch reference,
replacing them with a stable repository or commit link, or a concise
implementation-status statement.

In `@src/vouch/health.py`:
- Around line 576-626: Wire the existing _tick progress helper into
rebuild_index’s stages: call it with "claims", "pages", and "entities" before
their respective indexing loops, and with "embeddings" before
_rebuild_embeddings(store). Preserve the documented phase labels and on_progress
behavior.
- Around line 629-651: Update _rebuild_embeddings to populate the
embedding_index table used by search_semantic() in addition to the legacy
embeddings table written by index_db.index_embedding(). After generating each
vector, invoke the appropriate embedding_index insertion/upsert helper with the
same kind, entity ID, and vector, ensuring rebuilt indexes immediately support
backend=embedding searches.

In `@src/vouch/index_db.py`:
- Around line 134-167: The rebuild and semantic search paths use different
embedding tables, so reindexing leaves embedding search empty. Update the
rebuild logic and its embedding write helper, including index_embedding, to
populate the embedding_index table consumed by search_semantic/search_embedding,
or consolidate both paths onto one shared table and update all related queries
consistently.

In `@src/vouch/jsonl_server.py`:
- Around line 713-726: Update _h_propose_theme to use _agent() as the fallback
when resolving actor, while preserving an explicitly supplied p["agent"] value.
Replace the environment-variable fallback so the HTTP transport’s _actor
ContextVar and X-Vouch-Agent attribution are honored consistently in
themes.propose_theme.

In `@tests/embeddings/test_integration.py`:
- Around line 48-72: Ensure test_semantic_search_finds_lexically_disjoint_claim
registers the mock embedder used by the other embedding tests before calling
index_db.search_semantic, or skip the test with importorskip when the real
embeddings dependency is unavailable. Keep the assertion unchanged once search
is guaranteed to return results.

---

Outside diff comments:
In @.vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl:
- Around line 1-94: Remove the tracked capture buffer under .vouch/captures from
the commit and ensure it is excluded by the repository’s ignore rules. If it
exists in published history, purge the file from all repository history and
force-push the cleaned history as required; verify no credential-store
references or other sensitive capture data remain tracked.

---

Nitpick comments:
In `@src/vouch/sessions.py`:
- Around line 37-38: The volunteer hook currently propagates setup exceptions
after the session is persisted, preventing subsequent session initialization. In
session_start(), wrap the entire volunteer_context.on_session_start(store, sess)
call in the existing best-effort exception handling, including
hot_memory.register() and _start_watch() failures, so exceptions are logged or
swallowed consistently and the function still returns sess for
register_mcp_push() to run.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f56e3d1d-9b90-4f73-a0ad-04b6f3bcacb2

📥 Commits

Reviewing files that changed from the base of the PR and between 5c98a3a and 71db0a8.

⛔ Files ignored due to path filters (6)
  • desktop/docs/screenshots/browse.png is excluded by !**/*.png
  • desktop/docs/screenshots/dashboard.png is excluded by !**/*.png
  • desktop/docs/screenshots/dual-solve.png is excluded by !**/*.png
  • desktop/docs/screenshots/review.png is excluded by !**/*.png
  • desktop/package-lock.json is excluded by !**/package-lock.json
  • docs/banner.svg is excluded by !**/*.svg
📒 Files selected for processing (123)
  • .github/workflows/security-audit.yml
  • .pre-commit-config.yaml
  • .vouch/captures/1218169c-de25-48f4-bd93-70ff77d2fda2.jsonl
  • .vouch/captures/5720220c-b77c-4947-aeec-9d6ef91d70b8.jsonl
  • .vouch/captures/8a6df051-6c56-4d86-b0ed-66f6638ecfdc.jsonl
  • .vouch/captures/ec4b8842-f167-467e-a33c-dc87911c9e0e.jsonl
  • .vouch/claims/vouch-starter-reviewed-knowledge.yaml
  • .vouch/decided/20260707-092531-154cb163.yaml
  • .vouch/decided/20260707-093005-05345f47.yaml
  • .vouch/decided/20260707-093125-4d6972f9.yaml
  • .vouch/pages/edit-in-obsidian.md
  • .vouch/pages/reviewed-knowledge-store.md
  • .vouch/pages/session-what-are-the-non-development-related-files-and-folde.md
  • .vouch/schema_version
  • .vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/content
  • .vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/meta.yaml
  • CHANGELOG.md
  • CONTRIBUTING.md
  • README.md
  • desktop/.gitignore
  • desktop/CHANGELOG.md
  • desktop/LICENSE
  • desktop/README.md
  • desktop/docs/architecture.md
  • desktop/docs/reports/2026-06-26.md
  • desktop/electron-builder.yml
  • desktop/electron.vite.config.ts
  • desktop/package.json
  • desktop/resources/vouch/.gitkeep
  • desktop/scripts/dev-with-vouch.ts
  • desktop/scripts/gen-methods.ts
  • desktop/src/catalog/backend.json
  • desktop/src/catalog/methods.json
  • desktop/src/main/http-client.ts
  • desktop/src/main/index.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/jsonl-client.ts
  • desktop/src/main/kb-store.ts
  • desktop/src/main/supervisor.ts
  • desktop/src/main/tray.ts
  • desktop/src/main/types.ts
  • desktop/src/main/vouch-locator.ts
  • desktop/src/preload/index.ts
  • desktop/src/renderer/index.html
  • desktop/src/renderer/src/App.tsx
  • desktop/src/renderer/src/app.css
  • desktop/src/renderer/src/components/Diff.tsx
  • desktop/src/renderer/src/components/Drawer.tsx
  • desktop/src/renderer/src/components/EmptyState.tsx
  • desktop/src/renderer/src/components/MethodCard.tsx
  • desktop/src/renderer/src/components/MethodForm.tsx
  • desktop/src/renderer/src/components/Placeholder.tsx
  • desktop/src/renderer/src/components/Rail.tsx
  • desktop/src/renderer/src/components/StatusBar.tsx
  • desktop/src/renderer/src/components/Topbar.tsx
  • desktop/src/renderer/src/components/controls/index.tsx
  • desktop/src/renderer/src/components/results/Cards.tsx
  • desktop/src/renderer/src/components/results/JsonTree.tsx
  • desktop/src/renderer/src/components/results/Renderers.tsx
  • desktop/src/renderer/src/components/results/ResultView.tsx
  • desktop/src/renderer/src/components/results/atoms.tsx
  • desktop/src/renderer/src/global.d.ts
  • desktop/src/renderer/src/lib/VouchContext.tsx
  • desktop/src/renderer/src/lib/client.ts
  • desktop/src/renderer/src/lib/format.ts
  • desktop/src/renderer/src/lib/useOnOpen.tsx
  • desktop/src/renderer/src/lib/useVouchEvents.ts
  • desktop/src/renderer/src/main.tsx
  • desktop/src/renderer/src/views/Dashboard.tsx
  • desktop/src/renderer/src/views/DualSolve.tsx
  • desktop/src/renderer/src/views/GenericView.tsx
  • desktop/src/renderer/src/views/Review.tsx
  • desktop/src/renderer/src/views/blurbs.ts
  • desktop/src/renderer/src/views/registry.tsx
  • desktop/src/shared/ipc.ts
  • desktop/src/shared/methods.gen.ts
  • desktop/src/shared/methods.types.ts
  • desktop/test/catalog.test.ts
  • desktop/test/controls.test.tsx
  • desktop/test/gen-methods.test.ts
  • desktop/test/http-client.test.ts
  • desktop/test/jsonl-client.test.ts
  • desktop/test/method-card.test.tsx
  • desktop/test/method-form.test.tsx
  • desktop/test/method-gate.test.ts
  • desktop/test/smoke-jsonl.ts
  • desktop/test/vouch-locator.test.ts
  • desktop/tsconfig.json
  • desktop/tsconfig.node.json
  • desktop/tsconfig.scripts.json
  • desktop/tsconfig.web.json
  • desktop/vitest.config.ts
  • docs/superpowers/specs/2026-07-06-review-ui-multi-kb-design.md
  • migrations/README.md
  • spec/2026-05-21/README.md
  • spec/2026-05-21/SPEC.md
  • spec/2026-05-21/audit-vocabulary.md
  • spec/2026-05-21/methods.md
  • spec/2026-05-21/retrieval.md
  • spec/2026-05-21/review-gate.md
  • spec/2026-05-21/transports.md
  • spec/README.md
  • spec/audit-vocabulary.md
  • spec/methods.md
  • spec/retrieval.md
  • spec/review-gate.md
  • spec/transports.md
  • src/vouch/bundle.py
  • src/vouch/cli.py
  • src/vouch/compile.py
  • src/vouch/context.py
  • src/vouch/embeddings/base.py
  • src/vouch/health.py
  • src/vouch/index_db.py
  • src/vouch/jsonl_server.py
  • src/vouch/server.py
  • src/vouch/sessions.py
  • src/vouch/storage.py
  • tests/embeddings/conftest.py
  • tests/embeddings/test_integration.py
  • tests/embeddings/test_search.py
  • tests/test_cli.py
  • tests/test_compile.py
💤 Files with no reviewable changes (87)
  • spec/methods.md
  • desktop/.gitignore
  • desktop/electron-builder.yml
  • .pre-commit-config.yaml
  • desktop/test/gen-methods.test.ts
  • desktop/test/http-client.test.ts
  • desktop/CHANGELOG.md
  • desktop/README.md
  • desktop/test/method-gate.test.ts
  • desktop/docs/architecture.md
  • desktop/src/main/kb-store.ts
  • migrations/README.md
  • desktop/tsconfig.scripts.json
  • desktop/electron.vite.config.ts
  • desktop/src/renderer/src/components/Topbar.tsx
  • desktop/src/renderer/index.html
  • desktop/src/main/supervisor.ts
  • desktop/src/renderer/src/components/results/JsonTree.tsx
  • desktop/package.json
  • spec/2026-05-21/README.md
  • spec/README.md
  • desktop/src/renderer/src/app.css
  • desktop/src/renderer/src/views/Dashboard.tsx
  • spec/transports.md
  • desktop/LICENSE
  • desktop/src/renderer/src/lib/format.ts
  • desktop/src/renderer/src/components/Drawer.tsx
  • desktop/src/renderer/src/components/Rail.tsx
  • desktop/docs/reports/2026-06-26.md
  • desktop/scripts/gen-methods.ts
  • desktop/test/catalog.test.ts
  • desktop/src/renderer/src/views/registry.tsx
  • spec/2026-05-21/transports.md
  • desktop/src/main/ipc.ts
  • desktop/src/renderer/src/components/Placeholder.tsx
  • desktop/src/renderer/src/global.d.ts
  • desktop/src/renderer/src/views/DualSolve.tsx
  • spec/2026-05-21/audit-vocabulary.md
  • desktop/src/renderer/src/components/Diff.tsx
  • desktop/test/jsonl-client.test.ts
  • spec/retrieval.md
  • spec/2026-05-21/methods.md
  • desktop/src/main/index.ts
  • desktop/src/shared/methods.types.ts
  • desktop/src/renderer/src/main.tsx
  • desktop/vitest.config.ts
  • desktop/src/renderer/src/components/MethodForm.tsx
  • desktop/test/controls.test.tsx
  • spec/review-gate.md
  • desktop/tsconfig.node.json
  • desktop/tsconfig.json
  • spec/audit-vocabulary.md
  • desktop/src/renderer/src/components/EmptyState.tsx
  • desktop/src/renderer/src/views/blurbs.ts
  • desktop/src/catalog/methods.json
  • spec/2026-05-21/review-gate.md
  • desktop/src/shared/methods.gen.ts
  • desktop/src/main/http-client.ts
  • desktop/src/renderer/src/components/results/ResultView.tsx
  • spec/2026-05-21/SPEC.md
  • desktop/src/main/vouch-locator.ts
  • desktop/src/main/tray.ts
  • desktop/src/main/types.ts
  • desktop/src/renderer/src/views/GenericView.tsx
  • desktop/src/renderer/src/components/MethodCard.tsx
  • desktop/src/renderer/src/components/StatusBar.tsx
  • desktop/src/renderer/src/views/Review.tsx
  • desktop/src/shared/ipc.ts
  • desktop/src/renderer/src/components/results/atoms.tsx
  • desktop/src/renderer/src/lib/useOnOpen.tsx
  • desktop/scripts/dev-with-vouch.ts
  • desktop/src/catalog/backend.json
  • desktop/src/renderer/src/App.tsx
  • desktop/test/method-card.test.tsx
  • desktop/src/main/jsonl-client.ts
  • desktop/src/renderer/src/components/controls/index.tsx
  • desktop/test/method-form.test.tsx
  • desktop/test/smoke-jsonl.ts
  • desktop/tsconfig.web.json
  • desktop/src/renderer/src/lib/client.ts
  • desktop/test/vouch-locator.test.ts
  • desktop/src/preload/index.ts
  • spec/2026-05-21/retrieval.md
  • desktop/src/renderer/src/lib/VouchContext.tsx
  • desktop/src/renderer/src/components/results/Cards.tsx
  • desktop/src/renderer/src/lib/useVouchEvents.ts
  • desktop/src/renderer/src/components/results/Renderers.tsx
🛑 Comments failed to post (12)
.github/workflows/security-audit.yml (2)

25-40: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not mask audit setup failures.

Job-level continue-on-error also hides checkout, dependency installation, and audit setup failures, allowing the workflow to appear successful without scanning. Keep setup failures fatal and scope the non-blocking behavior to the pip-audit step only.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for 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.

In @.github/workflows/security-audit.yml around lines 25 - 40, Remove the
job-level continue-on-error setting so checkout, Python setup, and dependency
installation failures remain fatal. Add continue-on-error only to the step named
“audit” that runs pip-audit, preserving non-blocking audit findings without
masking setup failures.

27-27: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable checkout credential persistence.

actions/checkout leaves GITHUB_TOKEN in local Git configuration by default. Since the workflow later executes installation code, compromised repository or build code could access that token. Set persist-credentials: false.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for 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.

In @.github/workflows/security-audit.yml at line 27, Update the
actions/checkout@v4 step to set persist-credentials: false, preventing the
GITHUB_TOKEN from being stored in local Git configuration before installation
code runs.

Source: Linters/SAST tools

.vouch/decided/20260707-092531-154cb163.yaml (1)

42-45: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Record an actionable rejection reason.

decision_reason: 'yes' does not explain why this proposal was rejected, making the review decision difficult to audit or act on. Store the actual rejection rationale.

🤖 Prompt for 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.

In @.vouch/decided/20260707-092531-154cb163.yaml around lines 42 - 45, Replace
the non-actionable decision_reason value “yes” with a concise, specific
explanation of why the proposal was rejected, preserving the existing status and
decision metadata.
.vouch/pages/session-what-are-the-non-development-related-files-and-folde.md (2)

36-38: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Redact host-specific paths before committing session pages.

This page exposes /home/a/.claude/... and /tmp/claude-1000/..., leaking the local username, filesystem layout, and names of private memory or scratchpad artifacts. Store repository-relative or redacted paths instead; the same capture data is also present in the corresponding decided record.

🤖 Prompt for 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.

In @.vouch/pages/session-what-are-the-non-development-related-files-and-folde.md
around lines 36 - 38, Replace the host-specific absolute paths in the session
page with repository-relative or otherwise redacted paths, removing usernames,
temporary directories, and private memory or scratchpad filenames. Also update
the corresponding decided record containing the same capture data to apply the
same redaction.

42-42: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Normalize the captured Markdown.

The fenced block lacks a language identifier, and the command code span contains invalid spacing, triggering MD040 and MD038. Fix the capture formatter or normalize this artifact before committing.

Also applies to: 68-68

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 42-42: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for 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.

In @.vouch/pages/session-what-are-the-non-development-related-files-and-folde.md
at line 42, Normalize the captured Markdown in the affected fenced blocks: add a
language identifier to each fence to satisfy MD040, and remove invalid leading
or trailing spaces from the command inline code spans to satisfy MD038. Apply
the same cleanup to both referenced occurrences.

Source: Linters/SAST tools

CONTRIBUTING.md (1)

128-130: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Separate webapp evidence from CLI-only changes.

This currently requires vouch-ui screenshots or recordings even for CLI-output changes, where no corresponding webapp state may exist. Require vouch-ui evidence for webapp changes and terminal captures for CLI changes; also use “screen recording” and “web app.”

🧰 Tools
🪛 LanguageTool

[grammar] ~128-~128: Ensure spelling is correct
Context: ... commits. - For changes that affect the webapp, CLI output, or user-facing behavior: ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~129-~129: Ensure spelling is correct
Context: ...ts or screenrecording from the vouch-ui webapp showing the behavior working as inten...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for 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.

In `@CONTRIBUTING.md` around lines 128 - 130, The contribution guidelines should
distinguish evidence requirements by change type: require vouch-ui screenshots
or screen recording for web app changes, and terminal captures for CLI-output
changes. Update the wording to use “screen recording” and “web app,” without
requiring web-app evidence for CLI-only changes.

Source: Linters/SAST tools

docs/superpowers/specs/2026-07-06-review-ui-multi-kb-design.md (1)

3-9: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Remove local filesystem and unpushed-branch details.

The committed spec includes ~/Dev/plind-junior/... and an unpushed branch reference. This leaks local environment information, is not reproducible for readers, and will become stale; replace it with a stable repository/commit link or a concise implementation-status statement.

🤖 Prompt for 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.

In `@docs/superpowers/specs/2026-07-06-review-ui-multi-kb-design.md` around lines
3 - 9, The spec metadata includes non-reproducible local filesystem and unpushed
branch details. Update the introductory status text in the review-ui multi-KB
design spec to remove the local path and branch reference, replacing them with a
stable repository or commit link, or a concise implementation-status statement.
src/vouch/health.py (2)

576-626: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

_tick progress callback is never invoked.

_tick is defined but never called anywhere in rebuild_index, so the documented on_progress phase labels ("claims", "pages", "entities", "embeddings") never fire. Either wire _tick(...) into each stage or drop the dead helper and the docstring promise.

🔧 Proposed wiring
     index_db.reset(store.kb_dir)
     with index_db.open_db(store.kb_dir) as conn:
+        _tick("claims")
         for c in store.list_claims():
             index_db.index_claim(
                 conn,
                 id=c.id,
                 text=c.text,
                 type=c.type.value,
                 status=c.status.value,
                 tags=c.tags,
             )
+        _tick("pages")
         for p in store.list_pages():
             index_db.index_page(
                 conn,
                 id=p.id,
                 title=p.title,
                 body=p.body,
                 type=p.type,
                 tags=p.tags,
             )
+        _tick("entities")
         for e in store.list_entities():
             index_db.index_entity(
                 conn,
                 id=e.id,
                 name=e.name,
                 description=e.description,
                 type=e.type.value,
                 aliases=e.aliases,
             )
+    _tick("embeddings")
     _rebuild_embeddings(store)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def _tick(phase: str) -> None:
        if on_progress is not None:
            on_progress(phase)

    # Detect a stale embedding-model identity before reset() wipes the meta.
    try:
        from . import audit
        from .embeddings.migration import detect_mismatch

        m = detect_mismatch(store.kb_dir)
        if m is not None:
            audit.log_event(
                store.kb_dir,
                event="embedding.model_mismatch",
                actor="vouch-health",
                object_ids=[],
                data=m,
            )
    except ImportError:
        pass
    index_db.reset(store.kb_dir)
    with index_db.open_db(store.kb_dir) as conn:
        _tick("claims")
        for c in store.list_claims():
            index_db.index_claim(
                conn,
                id=c.id,
                text=c.text,
                type=c.type.value,
                status=c.status.value,
                tags=c.tags,
            )
        _tick("pages")
        for p in store.list_pages():
            index_db.index_page(
                conn,
                id=p.id,
                title=p.title,
                body=p.body,
                type=p.type,
                tags=p.tags,
            )
        _tick("entities")
        for e in store.list_entities():
            index_db.index_entity(
                conn,
                id=e.id,
                name=e.name,
                description=e.description,
                type=e.type.value,
                aliases=e.aliases,
            )
    _tick("embeddings")
    _rebuild_embeddings(store)
    return index_db.stats(store.kb_dir)
🤖 Prompt for 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.

In `@src/vouch/health.py` around lines 576 - 626, Wire the existing _tick progress
helper into rebuild_index’s stages: call it with "claims", "pages", and
"entities" before their respective indexing loops, and with "embeddings" before
_rebuild_embeddings(store). Preserve the documented phase labels and on_progress
behavior.

629-651: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Which table does semantic search read, and does index_embedding target embeddings vs embedding_index?
rg -nP 'def (search_embedding|search_semantic|index_embedding|put_embedding|get_embedding)\b' src/vouch/index_db.py
rg -nP 'FROM (embeddings|embedding_index)\b' src/vouch/index_db.py
sed -n '110,140p' src/vouch/index_db.py

Repository: vouchdev/vouch

Length of output: 1919


🏁 Script executed:

#!/bin/bash
sed -n '400,500p' src/vouch/index_db.py
rg -n "embedding_index|search_semantic|search_embedding|rebuild_index|rebuild_embeddings|backend" tests src -g '!**/__pycache__/**'

Repository: vouchdev/vouch

Length of output: 24250


🏁 Script executed:

#!/bin/bash
sed -n '568,660p' src/vouch/health.py
sed -n '1,120p' src/vouch/index_db.py
rg -n "\bembeddings\b" src tests -g '!**/__pycache__/**'

Repository: vouchdev/vouch

Length of output: 17391


Populate embedding_index during _rebuild_embeddings rebuild_index() clears the table that search_semantic() reads, but this helper only writes the legacy embeddings table via index_db.index_embedding(). That leaves backend=embedding searches empty after a rebuild until a separate backfill runs.

🤖 Prompt for 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.

In `@src/vouch/health.py` around lines 629 - 651, Update _rebuild_embeddings to
populate the embedding_index table used by search_semantic() in addition to the
legacy embeddings table written by index_db.index_embedding(). After generating
each vector, invoke the appropriate embedding_index insertion/upsert helper with
the same kind, entity ID, and vector, ensuring rebuilt indexes immediately
support backend=embedding searches.

Source: Linters/SAST tools

src/vouch/index_db.py (1)

134-167: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Who reads the new `embeddings` table vs `embedding_index`?
echo "== callers of search_embeddings (new table) =="
rg -nP '\bsearch_embeddings\s*\(' --type=py -g '!**/index_db.py'
echo "== callers of search_embedding / embedding_index path =="
rg -nP '\bsearch_embedding\s*\(' --type=py
echo "== writers of embedding_index =="
rg -nP '\b(put_embedding|index_embedding)\s*\(' --type=py
echo "== what rebuild_index repopulates =="
ast-grep run --pattern 'def rebuild_index($$$) { $$$ }' --lang python src/vouch/health.py || sed -n '560,655p' src/vouch/health.py

Repository: vouchdev/vouch

Length of output: 199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== file list =='
git ls-files 'src/vouch/*'

echo '== search for embedding-related symbols =='
rg -n --hidden --glob '!**/.git/**' '\b(search_embeddings|search_embedding|index_embedding|put_embedding|embedding_index|embeddings)\b' src/vouch

echo '== outline src/vouch/index_db.py =='
ast-grep outline src/vouch/index_db.py --view expanded || true

echo '== outline src/vouch/health.py =='
ast-grep outline src/vouch/health.py --view expanded || true

echo '== outline src/vouch/storage.py =='
ast-grep outline src/vouch/storage.py --view expanded || true

Repository: vouchdev/vouch

Length of output: 17129


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== src/vouch/health.py 534-655 =='
sed -n '534,655p' src/vouch/health.py

echo '== src/vouch/index_db.py 134-175 =='
sed -n '134,175p' src/vouch/index_db.py

echo '== src/vouch/index_db.py 321-500 =='
sed -n '321,500p' src/vouch/index_db.py

echo '== src/vouch/storage.py 741-805 =='
sed -n '741,805p' src/vouch/storage.py

Repository: vouchdev/vouch

Length of output: 14981


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== search_semantic callers =='
rg -n '\bsearch_semantic\s*\(' src/vouch

echo '== surrounding fallback logic in likely callers =='
sed -n '150,240p' src/vouch/proposals.py
sed -n '160,230p' src/vouch/triage.py
sed -n '130,240p' src/vouch/embeddings/similarity.py

Repository: vouchdev/vouch

Length of output: 8134


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== callers of rebuild_index =='
rg -n '\brebuild_index\s*\(' src/vouch

echo '== any direct callers of search_embeddings =='
rg -n '\bsearch_embeddings\s*\(' src/vouch

echo '== search/semantic caller contexts around index_db.search_semantic =='
sed -n '120,210p' src/vouch/jsonl_server.py
sed -n '150,210p' src/vouch/server.py
sed -n '2250,2345p' src/vouch/cli.py
sed -n '1,130p' src/vouch/context.py

Repository: vouchdev/vouch

Length of output: 14588


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== src/vouch/cli.py 2670-2705 =='
sed -n '2670,2705p' src/vouch/cli.py

echo '== src/vouch/embeddings/migration.py outline =='
ast-grep outline src/vouch/embeddings/migration.py --view expanded || true

echo '== relevant embedding migration functions =='
rg -n '\b(backfill_embeddings|detect_mismatch|migrate|embedding_index)\b' src/vouch/embeddings/migration.py src/vouch/cli.py src/vouch/health.py

Repository: vouchdev/vouch

Length of output: 2877


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== src/vouch/embeddings/migration.py 1-120 =='
sed -n '1,120p' src/vouch/embeddings/migration.py

echo '== src/vouch/index_db.py schema area 1-80 =='
sed -n '1,80p' src/vouch/index_db.py

Repository: vouchdev/vouch

Length of output: 4933


Unify the embedding store used by rebuild and search
index_embedding writes to embeddings, but search_semantic/search_embedding still read embedding_index. A plain vouch reindex therefore rebuilds FTS5 only; embedding-based search stays empty until backfill_embeddings runs. Make the rebuild path populate the same table the semantic path reads, or collapse the two stores.

🧰 Tools
🪛 ast-grep (0.44.1)

[info] 137-137: use jsonify instead of json.dumps for JSON output
Context: json.dumps(vec)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🤖 Prompt for 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.

In `@src/vouch/index_db.py` around lines 134 - 167, The rebuild and semantic
search paths use different embedding tables, so reindexing leaves embedding
search empty. Update the rebuild logic and its embedding write helper, including
index_embedding, to populate the embedding_index table consumed by
search_semantic/search_embedding, or consolidate both paths onto one shared
table and update all related queries consistently.
src/vouch/jsonl_server.py (1)

713-726: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

_h_propose_theme bypasses per-request actor attribution.

actor = p.get("agent") or os.environ.get("VOUCH_AGENT", ...) skips _agent(), so the HTTP transport's _actor ContextVar (set from the X-Vouch-Agent header) is ignored and theme proposals are misattributed in the audit log. Use _agent() as the fallback for consistency with every other handler.

🔧 Proposed fix
-    actor = p.get("agent") or os.environ.get("VOUCH_AGENT", "unknown-agent")
+    actor = p.get("agent") or _agent()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

def _h_propose_theme(p: dict) -> dict:
    from . import themes

    store = _store()
    actor = p.get("agent") or _agent()
    cluster = themes.ThemeCluster(
        entities=p["entities"],
        claim_ids=p["claim_ids"],
        session_ids=p.get("session_ids", []),
        score=float(p.get("score", 0.0)),
        session_count=len(p.get("session_ids", [])),
        claim_count=len(p["claim_ids"]),
    )
    return themes.propose_theme(store, cluster, proposed_by=actor)
🤖 Prompt for 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.

In `@src/vouch/jsonl_server.py` around lines 713 - 726, Update _h_propose_theme to
use _agent() as the fallback when resolving actor, while preserving an
explicitly supplied p["agent"] value. Replace the environment-variable fallback
so the HTTP transport’s _actor ContextVar and X-Vouch-Agent attribution are
honored consistently in themes.propose_theme.
tests/embeddings/test_integration.py (1)

48-72: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check lines 1-47 of test_integration.py for embedder registration setup
sed -n '1,47p' tests/embeddings/test_integration.py

Repository: vouchdev/vouch

Length of output: 1470


🏁 Script executed:

#!/bin/bash
sed -n '48,120p' tests/embeddings/test_integration.py
rg -n "def search_semantic|get_embedder|register.*embedder|_use_mock_embedder|embedder" vouch tests -g '!**/__pycache__/**'

Repository: vouchdev/vouch

Length of output: 4273


🏁 Script executed:

#!/bin/bash
sed -n '1,220p' tests/embeddings/conftest.py
printf '\n--- index_db ---\n'
sed -n '1,260p' vouch/index_db.py
printf '\n--- embeddings core ---\n'
sed -n '1,260p' vouch/embeddings/__init__.py
printf '\n--- search tests ---\n'
sed -n '1,120p' tests/embeddings/test_search.py

Repository: vouchdev/vouch

Length of output: 5717


🏁 Script executed:

#!/bin/bash
fd -a 'index_db.py|base.py|__init__.py' . | sed -n '1,40p'
printf '\n--- index_db candidates ---\n'
for f in $(fd -a 'index_db.py' .); do echo "### $f"; sed -n '1,260p' "$f"; done
printf '\n--- embeddings base/init candidates ---\n'
for f in $(fd -a 'base.py' .); do echo "### $f"; sed -n '1,260p' "$f"; done
for f in $(fd -a '__init__.py' . | grep '/embeddings/__init__.py'); do echo "### $f"; sed -n '1,260p' "$f"; done

Repository: vouchdev/vouch

Length of output: 12617


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path
p = Path('src/vouch/index_db.py')
text = p.read_text()
for key in ['def search_semantic', 'get_embedder(', 'except (KeyError, ImportError)', 'return []']:
    idx = text.find(key)
    print(f"\n### {key} @ {idx}")
    if idx != -1:
        start = text.rfind('\n', 0, idx)
        end = text.find('\n\n', idx)
        print(text[idx:end if end != -1 else len(text)])
PY

printf '\n--- semantic search references ---\n'
rg -n "search_semantic|get_embedder|DEFAULT_MODEL_NAME|register\\(" src tests -g '!**/__pycache__/**'

Repository: vouchdev/vouch

Length of output: 11574


Register an embedder or skip this test when embeddings aren’t available.
search_semantic() returns [] if no embedder is registered, so hits[0][1] can fail here unless this module installs a backend or gates the test on the real embedding dependency. Reuse the mock registration pattern from the other embedding tests, or add an importorskip for the embeddings extra.

🤖 Prompt for 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.

In `@tests/embeddings/test_integration.py` around lines 48 - 72, Ensure
test_semantic_search_finds_lexically_disjoint_claim registers the mock embedder
used by the other embedding tests before calling index_db.search_semantic, or
skip the test with importorskip when the real embeddings dependency is
unavailable. Keep the assertion unchanged once search is guaranteed to return
results.

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: S 50-199 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: compile_kb same-batch duplicate page titles bypass the collision guard, causing a silent overwrite on approval

1 participant