Skip to content

Feat/compile#362

Merged
plind-junior merged 5 commits into
testfrom
feat/compile
Jul 6, 2026
Merged

Feat/compile#362
plind-junior merged 5 commits into
testfrom
feat/compile

Conversation

@plind-junior

@plind-junior plind-junior commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

What changed

Why

What might break

VEP

Tests

  • make check passes locally (lint + mypy + pytest)
  • New / changed behaviour has a test
  • CHANGELOG.md updated under ## [Unreleased]

Summary by CodeRabbit

  • New Features

    • Added a new compile action to draft topic pages from approved claims.
    • Added a compile option in the review queue when compilation is configured.
    • Added support for JSON and dry-run output in the new compile flow.
  • Bug Fixes

    • Improved validation to drop drafts with missing citations, broken links, duplicate titles, or unsupported page types.
    • Added clearer success and error notices after compile runs.
    • Prevented concurrent compile runs from overlapping.

vouch compile hands the live approved claims to a deployment-configured
llm command (compile.llm_cmd in config.yaml), parses the drafted topic
pages, and files the survivors as pending page proposals by the
wiki-compiler actor. never calls approve() — the review gate is the
ingest review.

every citation is verified mechanically before filing: listed claim ids
must exist and be live, inline [claim: …] markers must be backed by a
listed claim, wikilinks must resolve against existing pages plus the
surviving batch (drops cascade to a fixpoint so a dangling link never
ships), and a title that collides with an existing page or a pending
draft is dropped — approve() routes colliding page ids through
update_page, so an unchecked collision would silently overwrite on
approval. re-running compile is idempotent against its own pending
drafts.

the review-ui queue gets a compile wiki button (shown once llm_cmd is
configured): posts land the drafts in the same queue, one compile runs
at a time per kb, failures surface as a notice, and each run appends a
compile.run audit event attributed to whoever triggered it.

subprocess i/o is explicit utf-8 (the default follows the locale and
mojibakes or crashes on latin-1 hosts), the llm runs in a throwaway cwd
so a cli that discovers per-project hooks cannot fire this project's
capture pipeline mid-compile, and a malformed compile: stanza degrades
to defaults instead of 500ing the queue that reads it per render.
local clients (vouch-ui) run against vouch serve and can only reach
features advertised on the kb.* wire, so compile joins the method
surface: kb_compile mcp tool, kb.compile jsonl handler, and the
capabilities METHODS entry — test_capabilities enforces the parity.
the cli command already existed, completing the four registration
sites.

semantics are unchanged from the compile module: the call blocks while
the deployment-configured llm drafts (same shape as a summarize call),
files survivors as pending page proposals by the wiki-compiler actor,
attributes the run to the calling agent in the compile.run audit
event, and never approves. compileerror surfaces as a clean
caller-visible error envelope rather than internal_error.
110-second walkthrough recorded live against a throwaway demo kb: auto
capture, one-click llm summary, the review gate, vouch compile turning
approved claims into cited topic pages, and the real vouch recall
digest closing the loop. 720p/3.4mb in docs/, poster frame links to it
from the readme. demo content is generic placeholder data only.
@plind-junior plind-junior changed the base branch from main to test July 6, 2026 00:04
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

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: c01a38a1-cd3d-4aec-ae37-c0f5c9842acd

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
📝 Walkthrough

Walkthrough

Introduces a vouch compile feature that drafts topic pages from approved, cited claims using a configurable LLM command, validating citations and wikilinks. Exposed via CLI, MCP tool, JSONL server, and a web UI "compile wiki" button, filing surviving drafts as PENDING page proposals. Includes documentation and tests.

Changes

Compile Pipeline and Interfaces

Layer / File(s) Summary
Compile core module
src/vouch/compile.py
Adds CompileConfig/CompileReport, load_config, build_prompt, run_llm, parse_drafts, _draft_problem, _first_dangling_link, and compile_kb orchestrating draft generation, validation, wikilink resolution, proposal filing, and audit logging.
Capability advertisement
src/vouch/capabilities.py
Adds "kb.compile" to the exposed METHODS list.
CLI command
src/vouch/cli.py
Adds vouch compile command with --dry-run, --max-pages, --llm-cmd, --json options.
JSONL server handler
src/vouch/jsonl_server.py
Adds _h_compile and registers "kb.compile" in HANDLERS.
MCP tool
src/vouch/server.py
Adds kb_compile MCP tool wrapping compile_kb.
Web UI
src/vouch/web/server.py, src/vouch/web/static/app.css, src/vouch/web/templates/queue.html
Adds compile status query params to the queue route, a locked POST /compile route, and compile-form/notice UI.
Docs and changelog
docs/compile.md, CHANGELOG.md, README.md
Documents compile configuration, usage, validation/drop rules, limitations, and adds workflow overview.
Tests
tests/test_compile.py, tests/test_web.py
Covers compile validation, collisions, cascades, config loading, JSONL integration, audit logging, and web UI/route behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant WebServer as compile_wiki (POST /compile)
  participant CompileLock
  participant compile_kb
  participant KBStore
  participant WebSocket as WS clients

  Browser->>WebServer: POST /compile
  WebServer->>CompileLock: acquire per-KB lock
  alt lock busy
    WebServer-->>Browser: redirect compile_error=already running
  else lock free
    WebServer->>compile_kb: compile_kb(store, actor, max_pages, dry_run)
    compile_kb->>KBStore: read approved claims and pages
    compile_kb->>compile_kb: build_prompt, run_llm, parse_drafts
    compile_kb->>compile_kb: validate citations and wikilinks
    compile_kb->>KBStore: propose_page for surviving drafts
    compile_kb->>KBStore: log_event(compile.run)
    compile_kb-->>WebServer: CompileReport
    WebServer->>WebSocket: notify refresh
    WebServer-->>Browser: redirect /?compiled=&dropped=
  end
Loading

Possibly related PRs

  • vouchdev/vouch#198: Extends the same review-ui web layer (src/vouch/web/server.py queue route and templates) that the compile wiki button and POST /compile route build on.

Suggested labels: adapters

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title names the new compile feature and matches the main change in the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/compile

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 cli command line interface review-ui browser review ui mcp mcp, jsonl, and http surfaces tests tests and fixtures size: XL 1000 or more changed non-doc lines labels Jul 6, 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: 2

🧹 Nitpick comments (1)
src/vouch/web/server.py (1)

531-572: 🩺 Stability & Availability | 🔵 Trivial

compile_lock is process-local — won't hold across multiple workers.

The "one compile at a time per KB" guarantee documented in the comment only holds within a single process. If this app is ever run with multiple worker processes (e.g., uvicorn --workers N), each worker gets its own asyncio.Lock(), so two workers could each accept a /compile POST concurrently, defeating the stated purpose (avoiding exhausted threadpool/LLM spend from concurrent runs).

Given the localhost-first, single-KB-root design elsewhere in this file, this is likely fine as-is, but worth confirming the intended deployment topology.

🤖 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/web/server.py` around lines 531 - 572, The compile guard in
compile_wiki uses a process-local asyncio.Lock, so the “one compile at a time
per KB” guarantee only works within a single worker. If this endpoint may run
under multiple processes, replace the local compile_lock with a cross-process
coordination mechanism in compile_wiki/app.state (or explicitly enforce
single-worker deployment); otherwise, if localhost/single-worker is the intended
model, keep the current lock but document that assumption alongside
compile_lock.
🤖 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 `@docs/compile.md`:
- Around line 9-13: The ASCII diagram fence in the compile documentation is
unlabeled and triggers markdownlint MD040; update the fenced block in the
diagram section to use a text language label so the diagram remains valid
markdown without lint warnings. Locate the fenced diagram near the compile flow
illustration and change the opening fence to a text-labeled fence while keeping
the diagram content unchanged.

In `@src/vouch/compile.py`:
- Around line 316-336: Guard against non-positive max_pages in the compile flow,
because compile() currently accepts cap values of 0 or less and then drops every
draft. Update the cap calculation in src/vouch/compile.py (around compile(),
build_prompt(), and the phase 1 survivor loop) to validate or clamp max_pages to
a sane minimum before it is used, so CLI/config values like 0 or negative cannot
silently produce an empty pass.

---

Nitpick comments:
In `@src/vouch/web/server.py`:
- Around line 531-572: The compile guard in compile_wiki uses a process-local
asyncio.Lock, so the “one compile at a time per KB” guarantee only works within
a single worker. If this endpoint may run under multiple processes, replace the
local compile_lock with a cross-process coordination mechanism in
compile_wiki/app.state (or explicitly enforce single-worker deployment);
otherwise, if localhost/single-worker is the intended model, keep the current
lock but document that assumption alongside compile_lock.
🪄 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: e44bc931-0d9f-4ef4-bef4-182790f7da64

📥 Commits

Reviewing files that changed from the base of the PR and between d80b1e2 and dff7691.

⛔ Files ignored due to path filters (2)
  • docs/img/how-it-works-poster.jpg is excluded by !**/*.jpg
  • docs/vouch-how-it-works.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (13)
  • CHANGELOG.md
  • README.md
  • docs/compile.md
  • src/vouch/capabilities.py
  • src/vouch/cli.py
  • src/vouch/compile.py
  • src/vouch/jsonl_server.py
  • src/vouch/server.py
  • src/vouch/web/server.py
  • src/vouch/web/static/app.css
  • src/vouch/web/templates/queue.html
  • tests/test_compile.py
  • tests/test_web.py

Comment thread docs/compile.md
Comment on lines +9 to +13
```
sessions / sources claims topic pages
───────────────────▶ gate ───────────▶ compile ───────────▶ gate ──▶ wiki
(capture) (approve) (LLM drafts) (approve)
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Label the ASCII diagram fence as text.

The unlabeled fence triggers markdownlint MD040. Mark it as text so the diagram stays valid markdown without lint noise.

💡 Proposed fix
-```
+```text
 sessions / sources          claims               topic pages
    ───────────────────▶ gate ───────────▶ compile ───────────▶ gate ──▶ wiki
         (capture)             (approve)      (LLM drafts)       (approve)
-```
+```
📝 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.

Suggested change
```
sessions / sources claims topic pages
───────────────────▶ gate ───────────▶ compile ───────────▶ gate ──▶ wiki
(capture) (approve) (LLM drafts) (approve)
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 9-9: 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 `@docs/compile.md` around lines 9 - 13, The ASCII diagram fence in the compile
documentation is unlabeled and triggers markdownlint MD040; update the fenced
block in the diagram section to use a text language label so the diagram remains
valid markdown without lint warnings. Locate the fenced diagram near the compile
flow illustration and change the opening fence to a text-labeled fence while
keeping the diagram content unchanged.

Source: Linters/SAST tools

Comment thread src/vouch/compile.py
Comment on lines +316 to +336
cap = max_pages if max_pages is not None else cfg.max_pages

prompt = build_prompt(store, max_pages=cap)
drafts = parse_drafts(run_llm(cmd, prompt, timeout_seconds=cfg.timeout_seconds))

report = CompileReport(drafts=drafts, dry_run=dry_run)

existing = store.list_pages()
taken_names = {p.title.strip().lower() for p in existing}
taken_names |= {p.id.strip().lower() for p in existing}
taken_names |= _pending_page_names(store)

# phase 1: per-draft validation + the cap. cap first-come: a draft past
# the cap is dropped even if an earlier one falls later, so the outcome
# doesn't depend on drop order.
survivors: list[tuple[dict[str, Any], str]] = []
for i, draft in enumerate(drafts):
title = str(draft.get("title") or f"draft {i}").strip()
if len(survivors) >= cap:
report.dropped.append({"title": title, "reason": f"over max_pages={cap}"})
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard against a non-positive cap.

A config max_pages: 0/negative (or CLI --max-pages 0) makes cap <= 0, so every draft is dropped with over max_pages=0 and the pass silently produces nothing. _coerce only rejects non-numeric values, not out-of-range ones. Consider clamping to a sane minimum.

🛡️ Proposed guard
     cap = max_pages if max_pages is not None else cfg.max_pages
+    if cap < 1:
+        raise CompileError(f"max_pages must be >= 1, got {cap}")
📝 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.

Suggested change
cap = max_pages if max_pages is not None else cfg.max_pages
prompt = build_prompt(store, max_pages=cap)
drafts = parse_drafts(run_llm(cmd, prompt, timeout_seconds=cfg.timeout_seconds))
report = CompileReport(drafts=drafts, dry_run=dry_run)
existing = store.list_pages()
taken_names = {p.title.strip().lower() for p in existing}
taken_names |= {p.id.strip().lower() for p in existing}
taken_names |= _pending_page_names(store)
# phase 1: per-draft validation + the cap. cap first-come: a draft past
# the cap is dropped even if an earlier one falls later, so the outcome
# doesn't depend on drop order.
survivors: list[tuple[dict[str, Any], str]] = []
for i, draft in enumerate(drafts):
title = str(draft.get("title") or f"draft {i}").strip()
if len(survivors) >= cap:
report.dropped.append({"title": title, "reason": f"over max_pages={cap}"})
continue
cap = max_pages if max_pages is not None else cfg.max_pages
if cap < 1:
raise CompileError(f"max_pages must be >= 1, got {cap}")
prompt = build_prompt(store, max_pages=cap)
drafts = parse_drafts(run_llm(cmd, prompt, timeout_seconds=cfg.timeout_seconds))
report = CompileReport(drafts=drafts, dry_run=dry_run)
existing = store.list_pages()
taken_names = {p.title.strip().lower() for p in existing}
taken_names |= {p.id.strip().lower() for p in existing}
taken_names |= _pending_page_names(store)
# phase 1: per-draft validation + the cap. cap first-come: a draft past
# the cap is dropped even if an earlier one falls later, so the outcome
# doesn't depend on drop order.
survivors: list[tuple[dict[str, Any], str]] = []
for i, draft in enumerate(drafts):
title = str(draft.get("title") or f"draft {i}").strip()
if len(survivors) >= cap:
report.dropped.append({"title": title, "reason": f"over max_pages={cap}"})
continue
🤖 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/compile.py` around lines 316 - 336, Guard against non-positive
max_pages in the compile flow, because compile() currently accepts cap values of
0 or less and then drops every draft. Update the cap calculation in
src/vouch/compile.py (around compile(), build_prompt(), and the phase 1 survivor
loop) to validate or clamp max_pages to a sane minimum before it is used, so
CLI/config values like 0 or negative cannot silently produce an empty pass.

approve and reject stamp decided_at with wall-clock time while the
digest window tests anchor to a fixed NOW; once real time passed
NOW + 1 day, test_build_window_excludes_old_decisions started failing
because fresh decisions now land inside the "future" window. pin
datetime.now in vouch.proposals to NOW for the fixture so decisions
are deterministic relative to the window under test.
Copilot AI review requested due to automatic review settings July 6, 2026 00:16
@plind-junior plind-junior merged commit 8bb9059 into test Jul 6, 2026
8 checks passed

Copilot AI 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.

Pull request overview

Adds a new “compile” surface to vouch (CLI / MCP / JSONL / review-ui) that runs a deployment-configured LLM command to draft wiki topic pages from approved claims, validates citations/links mechanically, and files surviving drafts as PENDING page proposals (keeping the review gate intact).

Changes:

  • Introduces vouch compile + kb.compile (MCP/JSONL) and a review-ui compile wiki button that runs the same ingest pass.
  • Adds the compiler implementation (src/vouch/compile.py) with JSON draft parsing, citation + wikilink validation, caps, and audit attribution.
  • Adds comprehensive test coverage for compile behavior (CLI/web/JSONL paths) and hardens time-dependent digest tests.

Reviewed changes

Copilot reviewed 14 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_web.py Adds review-ui compile button/route tests (visibility, redirects, queue effects, busy lock, audit attribution).
tests/test_digest.py Freezes proposal decision timestamps to keep digest window tests stable over time.
tests/test_compile.py New end-to-end tests for compile validation, dropping rules, JSONL wiring, and audit logging.
src/vouch/web/templates/queue.html Adds “compile wiki” form + success/error notices on the queue page.
src/vouch/web/static/app.css Styles compile button and queue notices.
src/vouch/web/server.py Adds /compile POST route, compile availability on GET /, and a per-app compile lock.
src/vouch/server.py Registers new MCP tool kb_compile.
src/vouch/jsonl_server.py Registers JSONL handler for kb.compile.
src/vouch/compile.py Implements compile pipeline: prompt build, LLM invocation, draft parsing, validation, proposal filing, audit event.
src/vouch/cli.py Adds vouch compile command with --dry-run/--max-pages/--llm-cmd/--json.
src/vouch/capabilities.py Adds kb.compile to the advertised method surface.
README.md Adds demo video section referencing compile in the full workflow.
docs/compile.md New user documentation for setup, behavior, validator rules, and limits.
CHANGELOG.md Adds release notes for compile + review-ui button.

Comment thread src/vouch/compile.py
Comment on lines +337 to +341
problem = _draft_problem(store, draft, taken_names=taken_names)
if problem:
report.dropped.append({"title": title, "reason": problem})
continue
survivors.append((draft, title))
Comment thread src/vouch/web/server.py
Comment on lines +553 to +567
if compile_lock.locked():
reason = quote("a compile is already running — refresh in a moment")
return RedirectResponse(url=f"/?compile_error={reason}", status_code=303)
async with compile_lock:
try:
report = await run_in_threadpool(
compile_mod.compile_kb, store,
config=cfg, triggered_by=reviewer(),
)
except compile_mod.CompileError as e:
reason = quote(str(e)[:200])
return RedirectResponse(
url=f"/?compile_error={reason}", status_code=303,
)
await _notify("queue", action="compile", proposed=len(report.proposed))
Comment on lines +21 to +22
{% if compiled is not none %}
<p class="notice notice-ok">compiled {{ compiled }} page draft(s) into the queue{% if compile_dropped %} · {{ compile_dropped }} dropped by citation checks{% endif %}.</p>
Comment thread src/vouch/jsonl_server.py
Comment on lines +371 to +377
try:
report = compile_mod.compile_kb(
_store(), triggered_by=_agent(),
max_pages=p.get("max_pages"),
dry_run=bool(p.get("dry_run", False)),
session_id=p.get("session_id"),
)
plind-junior added a commit that referenced this pull request Jul 6, 2026
a config max_pages: 0 (or --max-pages 0) made cap <= 0, so every
draft was dropped with "over max_pages=0" after the LLM run was
already spent. raise CompileError up front instead of silently
producing nothing.
plind-junior added a commit that referenced this pull request Jul 6, 2026
the unlabeled fence trips markdownlint MD040.
jsdevninja pushed a commit to jsdevninja/vouch that referenced this pull request Jul 6, 2026
a config max_pages: 0 (or --max-pages 0) made cap <= 0, so every
draft was dropped with "over max_pages=0" after the LLM run was
already spent. raise CompileError up front instead of silently
producing nothing.
jsdevninja pushed a commit to jsdevninja/vouch that referenced this pull request Jul 6, 2026
…iew)

the unlabeled fence trips markdownlint MD040.
jsdevninja pushed a commit to jsdevninja/vouch that referenced this pull request Jul 6, 2026
… review)

markdownlint md040 wants a language on the fence; matches how the
compile flow diagram was labelled after the vouchdev#362 review.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli command line interface docs documentation, specs, examples, and repo guidance mcp mcp, jsonl, and http surfaces review-ui browser review ui size: XL 1000 or more changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants