Skip to content

Fix/openclaw plugin manifest#363

Closed
plind-junior wants to merge 8 commits into
testfrom
fix/openclaw-plugin-manifest
Closed

Fix/openclaw plugin manifest#363
plind-junior wants to merge 8 commits into
testfrom
fix/openclaw-plugin-manifest

Conversation

@plind-junior

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

Copy link
Copy Markdown
Member

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 session capture and recall improvements, including start-from-session support, session summaries, merging pending items, and read/list commands for approved knowledge.
    • Expanded the web UI with merge actions, rooted navigation, and clearer review/session views.
    • Added OpenClaw plugin support updates and new session-related skills/commands.
  • Bug Fixes

    • Fixed auto-merge and session serialization issues that could interrupt workflows.
    • Improved handling of prompts, summaries, and multi-tenant routing.
  • Documentation

    • Updated getting-started, tutorials, README, and release notes with the new workflows and UI guidance.

batches the session-loop work accumulated on this branch: post-hoc llm
narratives for captured sessions (summarize.py + vouch summarize,
kb.summarize_session), two-stage session review surface
(kb.list_sessions stages), page-proposal merging (kb.merge_pending),
session seeding (vouch session start-from + the start_from review-ui
view), the UserPromptSubmit capture hook so spoken facts reach the
mechanical record, and the review-ui template/styling updates that
carry those flows. multi-tenant seams noted in pyproject stay in this
package; the hosted service lives elsewhere.
adds the vouch-start slash command / skill to the claude-code and
openclaw adapters, keeps the openclaw skill bodies mirrored to the
claude-code commands per the manifest sync rules, and updates the
install manifests and plugin packaging accordingly.
readme hero imagery and copy, the remember-across-sessions tutorial
with its measured a/b numbers, the company-brain / decision-distill /
vouch-cloud design specs, refreshed marketing site pages under web/,
the claude-code live presentation deck, regenerated example
screenshots, and the review-ui deployment screenshot moved under
docs/img/. placeholder data only.
adds the auto-label workflow and its composite action so incoming
contributions get the ai-involvement label applied mechanically.
.vouch/ carries this machine's own session summaries and audit trail —
operational personal data, not project source. gitignore it and drop
it from tracking so the kb stays local-first here the way vouch
recommends for any repo whose kb is private to its operator. history
retains the earlier snapshots.
@plind-junior
plind-junior changed the base branch from main to test July 6, 2026 00:20
@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: 2f1e292f-2233-4ed9-9a38-0ef0ae006d79

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

This PR reworks Claude Code session capture to file claim proposals (with prompt recording, transcript evidence, and optional LLM summarization), adds merging of pending proposals, session start-from context seeding, new MCP/CLI/JSONL surfaces, OpenClaw plugin packaging/id fixes, multi-tenant web UI routing, adapter docs/skills, example fixtures, CI workflows, and a marketing site redesign.

Changes

Core Feature: Session Claims, Merge, Summarize, OpenClaw, Web UI

Layer / File(s) Summary
Claim-based capture pipeline
src/vouch/capture.py, tests/test_capture.py
Session buffers now file CLAIM proposals with prompt recording, transcript source registration, and richer summary bodies.
Claim/capabilities contracts
src/vouch/models.py, src/vouch/capabilities.py
Adds ClaimType.SESSION and new kb.list_sessions/kb.summarize_session/kb.merge_pending capability methods.
Merge pending proposals
src/vouch/proposals.py, src/vouch/storage.py, tests/test_merge_pending.py
New merge_pending combines pending page/claim proposals, unions fields, rejects sources, and adds update_proposal storage support.
LLM session summarization
src/vouch/summarize.py, tests/test_summarize.py
New module generates/inserts AI narrative sections into pending session proposals via an external LLM command.
Session start-from seeding
src/vouch/sessions.py, tests/test_sessions.py
build_start_context seeds a new session from an approved/pending reference.
CLI command wiring
src/vouch/cli.py
Adds read/list, merge-pending, session start-from/list, capture prompt/sweep, summarize commands.
MCP/JSONL server & health/rpc fixes
src/vouch/server.py, src/vouch/jsonl_server.py, src/vouch/health.py, src/vouch/openclaw/rpc.py, tests/test_openclaw_context_engine.py
Adds new tools/handlers, request-scoped KB binding, last-audit timestamp, and datetime JSON serialization fix.
Multi-tenant web UI
src/vouch/web/server.py, src/vouch/web/templates/*, src/vouch/web/static/*, tests/test_web.py
Adds root-prefix routing, /merge and /start-from/{ref} routes, and reviewer attribution.
OpenClaw packaging fix
adapters/openclaw/vouch-context-engine.mjs, src/vouch/openclaw/context_engine.py, openclaw.plugin.json, package.json, tests/test_openclaw_plugin_manifest.py, tests/test_openclaw_plugin_load_real.py
Renames engine id to vouch, rewrites manifest to 2026.6 dialect, and validates via a real-load e2e test.
Adapter commands/skills/install manifests
adapters/claude-code/..., adapters/openclaw/..., .claude/settings.json, tests/test_install_adapter.py
Adds /vouch-start command/skill, updates recall/resolve-issue/status docs, and extends install tiers.
Documentation
README.md, CLAUDE.md, docs/getting-started.md, CHANGELOG.md
Documents new CLI surfaces, HTTP transport, and OpenClaw packaging invariants.

Example Decision-Log KB Fixture

Layer / File(s) Summary
Example KB content
examples/decision-log/vouch/*
New example config, claims, decided records, sources, pages, audit log, and schema version replacing prior superseded examples.

Design Specs, Presentation, and Tutorials

Layer / File(s) Summary
Presentation deck
docs/presentation/vouch-claude-code-live.html
New slide deck with styling and navigation script.
Company brain spec
docs/superpowers/specs/2026-07-04-company-brain-design.md
New design doc on the "brain pack" approach.
Decision-distill spec
docs/superpowers/specs/2026-07-04-decision-distill-design.md
New design doc on decision-claim distillation.
VouchHub cloud spec
docs/superpowers/specs/2026-07-05-vouch-cloud-design.md
New phase-2 hosted multi-tenant architecture doc.
Cross-session tutorial
docs/tutorials/README.md, docs/tutorials/remember-across-sessions.md
New tutorial with transcripts and benchmark results.

Marketing Website Redesign

Layer / File(s) Summary
Scripts and component CSS
web/app.js, web/app.css
Adds copy-to-clipboard and restyles terminal/gate/claim components.
Global design tokens
web/base.css
Replaces color tokens, typography, and section-shell layout.
Landing and content pages
web/index.html, web/how-it-works.html, web/reference.html, web/gittensor.html
Rewrites branding, navigation, and footer copy.

CI Workflows and Repo Infra

Layer / File(s) Summary
Auto-merge and triage workflows
.github/actions/create-auto-merge-pr/action.yml, .github/workflows/auto-label-ai-contribution.yml
New composite action and PR triage labeling workflow.
Repo housekeeping
.gitignore, .vouch/sources/.../content, pyproject.toml
Adds ignore rule, removes prior doc content, adds explanatory comment.

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

Possibly related issues

Possibly related PRs

  • vouchdev/vouch#198: Web review UI routing changes build directly on this PR's server/websocket implementation.
  • vouchdev/vouch#221: OpenClaw manifest/documentation changes build on this PR's documentation/manifest groundwork.
  • vouchdev/vouch#356: Both PRs modify src/vouch/capture.py's session summarization flow and transcript_path handling.

Suggested labels: openclaw, mcp, review-ui, size: XL

🚥 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 clearly matches the main change: fixing the OpenClaw plugin manifest.
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 fix/openclaw-plugin-manifest

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 ci github actions and automation cli command line interface review-ui browser review ui website static website adapters agent host adapters and install manifests openclaw openclaw integration mcp mcp, jsonl, and http surfaces storage kb storage, migrations, schemas, and proposals packaging packaging, build metadata, and make targets tests tests and fixtures size: XL 1000 or more changed non-doc lines labels Jul 6, 2026
…anifest

# Conflicts:
#	adapters/claude-code/install.yaml
#	docs/img/examples/decision-log-diff.svg
#	docs/img/examples/decision-log-search.svg
#	docs/tutorials/remember-across-sessions.md
#	openclaw.plugin.json
#	src/vouch/capture.py
#	src/vouch/cli.py
#	src/vouch/jsonl_server.py
#	src/vouch/server.py
#	src/vouch/web/server.py
#	tests/test_capture.py
#	tests/test_install_adapter.py
#	tests/test_openclaw_plugin_manifest.py
#	tests/test_web.py
Copilot AI review requested due to automatic review settings July 6, 2026 00:30

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

This PR significantly expands vouch’s session-capture/review surface and OpenClaw integration: captured sessions are now first-class claims (with transcript-source evidence), optional post-session LLM narrative enrichment is added, pending proposals can be merged, and the web review UI gains “start-from” plus tenant-mount rooting. It also updates the OpenClaw skill set (moving to /vouch-start) and adds GitHub automation workflows.

Changes:

  • Add captured-session summarization workflow (capture → optional LLM narrative; new summarize flows; session “start-from” seed output).
  • Add pending-proposal merge support across CLI/MCP/JSONL/web review UI (plus audit events).
  • Update OpenClaw/Claude Code adapters + manifests/skills, and add repo automation workflows.

Reviewed changes

Copilot reviewed 78 out of 85 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
web/reference.html Update site nav/branding/fonts/footer copy.
web/how-it-works.html Update site nav/branding/fonts/footer copy.
web/gittensor.html Update site nav/branding/fonts/footer copy.
web/app.js Add copy-button behavior; expand scroll-reveal selector set.
web/app.css Visual refresh for terminal/gate/catalogue styles and buttons.
tests/test_web.py Add web UI tests for merge and start-from routes.
tests/test_summarize.py New tests covering summarize config, transcript excerpting, insertion, and capture integration.
tests/test_sessions.py Add tests for session start-from context building across proposal/claim/page refs.
tests/test_openclaw_plugin_manifest.py Update expected OpenClaw skill list (swap to vouch-start).
tests/test_openclaw_plugin_load_real.py Include vouch-start in real plugin-load verification.
tests/test_merge_pending.py New tests for merging pending page proposals and claim proposals (audit + approvable).
tests/test_install_adapter.py Update expected installed command set and file counts (now 5 commands incl. vouch-start).
tests/test_capture.py Shift capture output expectations from page→claim; add prompt-recording, stable slug, list_sessions tests.
tests/test_brain_commands.py Remove tests specific to the removed “company-brain” command set.
src/vouch/web/templates/start_from.html New template rendering copyable start-from seed.
src/vouch/web/templates/source.html Root-prefix the “back to queue” link for tenant mounts.
src/vouch/web/templates/session.html Root-prefix links for tenant mounts; root-prefix proposal links.
src/vouch/web/templates/queue.html Add merge UI controls + root-prefix all queue actions/links.
src/vouch/web/templates/claim.html Root-prefix links/actions; add start-from link for session proposals.
src/vouch/web/templates/base.html Root-prefix static assets and nav; expose data-root for JS.
src/vouch/web/static/app.js Root-prefix WebSocket URL using body data-root.
src/vouch/web/static/app.css Add CSS for merge bar and start-from page elements.
src/vouch/web/server.py Add tenant-rooted redirects; add /merge and /start-from/{ref}; add reviewer attribution seam.
src/vouch/summarize.py New module implementing LLM narrative generation + transcript excerpt extraction.
src/vouch/storage.py Add starter config keys for summary settings; add KBStore.update_proposal().
src/vouch/sessions.py Add build_start_context() for “start-from” seed generation.
src/vouch/server.py Add kb.list_sessions, kb.summarize_session, kb.merge_pending MCP tools.
src/vouch/proposals.py Implement merge_pending for pages/claims; add heading demotion and merge audit events.
src/vouch/models.py Add ClaimType.SESSION.
src/vouch/jsonl_server.py Add request_context() for multi-tenant binding; add kb.list_sessions / kb.summarize_session / kb.merge_pending handlers.
src/vouch/health.py Add last_audit_at to status output.
src/vouch/cli.py Add merge-pending, session start-from, session list, capture prompt, summarize, capture sweep summarization flags; improve dedup ImportError UX.
src/vouch/capture.py Capture sessions now file CLAIM proposals with transcript Source evidence; add prompt recording; add list_sessions feed; add LLM enrichment hook points.
src/vouch/capabilities.py Register kb.list_sessions, kb.summarize_session, kb.merge_pending.
README.md Document http transport and updated OpenClaw skill count.
pyproject.toml Comment clarifying vouchhub (multi-tenant) seams are supported via vouch-kb[web].
openclaw.plugin.json Update description to “five vouch skills”.
docs/tutorials/remember-across-sessions.md Update install output example to include vouch-start.
docs/superpowers/specs/2026-07-05-vouch-cloud-design.md New draft spec describing vouchhub multi-tenant design.
docs/superpowers/specs/2026-07-04-decision-distill-design.md New draft spec describing decision distillation workflow.
docs/superpowers/specs/2026-07-04-company-brain-design.md New draft spec describing “company brain” direction.
docs/img/examples/decision-log-search.svg Update example SVG output and wording.
adapters/openclaw/skills/vouch-status/SKILL.md Update last-audit guidance to match kb_status output.
adapters/openclaw/skills/vouch-start/SKILL.md New OpenClaw skill for start-from seeding.
adapters/openclaw/skills/vouch-standup/SKILL.md Remove standup skill.
adapters/openclaw/skills/vouch-resolve-issue/SKILL.md Require registering evidence sources before proposing claims.
adapters/openclaw/skills/vouch-remember/SKILL.md Remove remember skill.
adapters/openclaw/skills/vouch-record/SKILL.md Remove record skill.
adapters/openclaw/skills/vouch-recall/SKILL.md Expand recall procedure to hybrid retrieval + neighbors + cited answers.
adapters/openclaw/skills/vouch-followup/SKILL.md Remove followup skill.
adapters/openclaw/skills/vouch-ask/SKILL.md Remove ask skill.
adapters/openclaw/install.yaml Update comments + add vouch-start command install entry.
adapters/openclaw/AGENTS.md.snippet Update listed slash commands to include /vouch-start.
adapters/claude-code/install.yaml Reduce T3 command set to 5 commands and include vouch-start.
adapters/claude-code/.claude/settings.json Add SessionEnd hook timeout comment and set timeout=120.
adapters/claude-code/.claude/commands/vouch-status.md Update last-audit guidance to match kb_status output.
adapters/claude-code/.claude/commands/vouch-start.md New Claude Code slash command for start-from seeding.
adapters/claude-code/.claude/commands/vouch-standup.md Remove standup command.
adapters/claude-code/.claude/commands/vouch-resolve-issue.md Require registering evidence sources before proposing claims.
adapters/claude-code/.claude/commands/vouch-remember.md Remove remember command.
adapters/claude-code/.claude/commands/vouch-record.md Remove record command.
adapters/claude-code/.claude/commands/vouch-recall.md Expand recall procedure to hybrid retrieval + neighbors + cited answers.
adapters/claude-code/.claude/commands/vouch-followup.md Remove followup command.
adapters/claude-code/.claude/commands/vouch-ask.md Remove ask command.
.gitignore Ignore .vouch directory at repo root.
.github/workflows/auto-label-ai-contribution.yml New workflow to apply needs-maintainer label to AI-contribution PRs.
.github/actions/create-auto-merge-pr/action.yml New composite action to create/update PRs and enable auto-merge.
.claude/settings.json Add UserPromptSubmit hook to record prompts; add SessionEnd hook timeout.
.vouch/sources/67478e72acfb8fac3a059143e95c95f5cc6f7e8d4dccc05fbcea8dbccb8a4eba/meta.yaml Remove committed KB source metadata (repo KB cleanup).
.vouch/sources/67478e72acfb8fac3a059143e95c95f5cc6f7e8d4dccc05fbcea8dbccb8a4eba/content Remove committed KB source content (repo KB cleanup).
.vouch/sources/06d8519f8dcf4149d23c8a48984541b2e9365ec364e7e58192e28ed149a2c47c/meta.yaml Remove committed transcript source metadata (repo KB cleanup).
.vouch/sources/06d8519f8dcf4149d23c8a48984541b2e9365ec364e7e58192e28ed149a2c47c/content Remove committed transcript source content (repo KB cleanup).
.vouch/decided/20260521-055206-7d6d92d6.yaml Remove committed decided proposal artifact (repo KB cleanup).
.vouch/config.yaml Remove committed KB config (repo KB cleanup).
.vouch/claims/vouch-uses-a-review-gated-proposal-workflow-agents-propose-c.yaml Remove committed claim artifact (repo KB cleanup).
.vouch/audit.log.jsonl Remove committed audit log (repo KB cleanup).
.vouch/.gitignore Remove committed KB gitignore (repo KB cleanup).

Comment thread src/vouch/summarize.py
Comment on lines +95 to +104
return SummaryConfig(
mode=mode,
llm_cmd=str(raw.get("summary_llm_cmd", "") or ""),
timeout_seconds=float(
raw.get("summary_timeout_seconds", DEFAULT_TIMEOUT_SECONDS)
),
max_transcript_chars=int(
raw.get("summary_max_transcript_chars", DEFAULT_MAX_TRANSCRIPT_CHARS)
),
)
Comment thread src/vouch/health.py
Comment on lines +46 to +66
def _last_audit_at(kb_dir: Path) -> str | None:
"""Timestamp of the newest audit event, without parsing the whole log.

The /vouch-status skill (and any at-a-glance tooling) renders a
"last audit" line; count_events alone can't supply it.
"""
log = kb_dir / "audit.log.jsonl"
try:
lines = log.read_text(encoding="utf-8").splitlines()
except OSError:
return None
for raw in reversed(lines):
line = raw.strip()
if not line:
continue
try:
created = json.loads(line).get("created_at")
except (ValueError, AttributeError):
return None
return str(created) if created else None
return None
Comment thread src/vouch/proposals.py
Comment on lines +719 to +721
claim_types = {str(p.payload.get("type", "")) for p in sources}
claim_type = claim_types.pop() if len(claim_types) == 1 else "observation"
confidence = min(float(p.payload.get("confidence", 0.7)) for p in sources)
Comment thread src/vouch/sessions.py
Comment on lines +195 to +203
seed = (
"you are starting a new session seeded from a previous session's "
f"summary (vouch {status} `{ref}`). continue where that session left "
"off: treat the record below as reviewed context, verify anything "
"that may have gone stale against the current repo state, and recall "
"more with kb_context or /vouch-recall before re-deriving.\n\n"
"---\n\n"
f"{body.strip()}\n"
)
Comment thread src/vouch/web/server.py
Comment on lines +556 to +569
@app.post("/merge", dependencies=guarded)
async def merge(
request: Request,
proposal_ids: Annotated[list[str], Form()],
reason: Annotated[str | None, Form()] = None,
) -> Any:
"""Merge the ticked pending page proposals into one combined pending
proposal (same code path as ``vouch merge-pending`` / kb.merge_pending);
the reviewer lands on the merged page to decide it."""
try:
merged = await run_in_threadpool(
proposals_mod.merge_pending_pages,
store, list(proposal_ids), merged_by=reviewer(), reason=reason,
)
Comment thread src/vouch/server.py
Comment on lines +612 to +617
"""Merge two or more pending page proposals into one combined proposal.

Files a NEW pending page carrying every source body and rejects the
originals with an audited pointer to it — the merged page still goes
through review like any other write.
"""
Comment on lines +22 to +29
<script>
document.getElementById("copy-seed").addEventListener("click", async () => {
const btn = document.getElementById("copy-seed");
await navigator.clipboard.writeText(document.getElementById("seed").value);
btn.textContent = "copied";
setTimeout(() => { btn.textContent = "copy seed"; }, 1500);
});
</script>
Comment thread openclaw.plugin.json
"name": "Vouch",
"version": "1.1.0",
"description": "Git-native, review-gated knowledge base. Registers vouch's context engine (cited retrieval + salience reflex + hot memory) and the vouch skills. The kb.* MCP server is deployment config: `openclaw mcp add vouch -- vouch serve`.",
"description": "Git-native, review-gated knowledge base. Registers vouch's context engine (cited retrieval + salience reflex + hot memory) and the five vouch skills. The kb.* MCP server is deployment config: `openclaw mcp add vouch -- vouch serve`.",

@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: 11

Caution

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

⚠️ Outside diff range comments (1)
tests/test_merge_pending.py (1)

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

Add JSONL coverage for kb.merge_pending
src/vouch/jsonl_server.py registers kb.merge_pending, but tests/test_jsonl_server.py doesn’t exercise its JSONL envelope. Add one request test for the {id, ok, result} success shape and one for {id, ok: false, error}.

🤖 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/test_merge_pending.py` around lines 1 - 188, Add JSONL request coverage
for the kb.merge_pending endpoint exposed by jsonl_server.register_kb_methods,
since the current tests only cover the underlying proposal merge logic. In
tests/test_jsonl_server.py, add one request asserting the success envelope
returned by kb.merge_pending includes id, ok, and a result payload, and another
asserting the failure path returns id, ok: false, and an error object. Use the
existing JSONL request helper and the kb.merge_pending method name to locate the
relevant test setup.

Source: Coding guidelines

🧹 Nitpick comments (10)
web/index.html (1)

248-252: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Font-size inconsistency vs. sibling pages.

This paragraph uses font-size:15px, while the equivalent footer "Install" paragraph on how-it-works.html, reference.html, and gittensor.html all use font-size:16px. Likely unintentional drift.

✏️ Suggested fix
-        <p style="font-size:15px; line-height:1.55;">
+        <p style="font-size:16px; line-height:1.55;">
🤖 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 `@web/index.html` around lines 248 - 252, The Install paragraph in this page is
using a smaller font size than the matching paragraph on the sibling pages,
creating an unintended style drift. Update the inline styling on the Install
section’s paragraph in the relevant HTML block so it matches the same font-size
used by the equivalent footer paragraph on how-it-works.html, reference.html,
and gittensor.html, keeping the rest of the markup unchanged.
src/vouch/summarize.py (1)

176-198: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Subprocess timeout may leave orphaned children when shell=True.

On TimeoutExpired, subprocess.run kills the shell process but not necessarily grandchildren spawned by config.llm_cmd (e.g. a long-running claude -p invocation that forks). For a summarizer command that hangs, this can leak processes across repeated vouch summarize/SessionEnd hook runs. Consider running in a new process group (start_new_session=True) and killing the group on timeout.

♻️ Proposed fix for process-group cleanup
     try:
         proc = subprocess.run(
             config.llm_cmd,
             shell=True,
             input=record,
             text=True,
             capture_output=True,
             timeout=config.timeout_seconds,
             env={**os.environ, "VOUCH_CAPTURE_OFF": "1"},
+            start_new_session=True,
         )
-    except (OSError, subprocess.SubprocessError):
+    except subprocess.TimeoutExpired as e:
+        if e.pid := getattr(e, "pid", None):
+            with contextlib.suppress(OSError, ProcessLookupError):
+                os.killpg(os.getpgid(e.pid), signal.SIGKILL)
+        return None
+    except (OSError, subprocess.SubprocessError):
         return None

Regarding the shell=True/unsanitized-command static-analysis flags on this block: the docstring already documents llm_cmd as deployment config at the same trust level as a git hook, so that part looks like an accepted design trade-off rather than a new issue.

🤖 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/summarize.py` around lines 176 - 198, The subprocess call in the
summarizer path can leave orphaned children when `config.llm_cmd` times out
because `subprocess.run` with `shell=True` only terminates the shell wrapper.
Update the `subprocess.run` invocation in the summarization helper to start a
new process group/session, and add timeout handling that explicitly kills the
whole group before returning None so repeated `vouch summarize` or `SessionEnd`
runs do not leak `claude -p` descendants.
docs/superpowers/specs/2026-07-04-decision-distill-design.md (1)

187-196: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

keep the llm config namespace singular.

please confirm whether distill.* is meant to be a future-only namespace. the shipped code already uses capture.summary_* in src/vouch/storage.py and src/vouch/summarize.py, so adding a second family here will split the deployment contract unless the migration path is spelled out.

🤖 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-04-decision-distill-design.md` around lines
187 - 196, The config namespace is being introduced inconsistently: this spec
adds `distill.*` while the shipped code already uses `capture.summary_*` in
`storage.py` and `summarize.py`. Update the spec to use the existing namespace,
or explicitly mark `distill.*` as future-only and document the migration path;
reference the `_starter_config` suggestion and the `capture.load_config`
template so the naming stays singular across the deployment contract.
.github/actions/create-auto-merge-pr/action.yml (1)

24-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider exposing PR number/URL as composite-action outputs.

The create-pull-request step already produces pull-request-number/pull-request-url, but the composite action doesn't re-expose them via an outputs: block, so calling workflows can't consume them without duplicating step IDs.

🤖 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/actions/create-auto-merge-pr/action.yml around lines 24 - 51, The
composite action currently hides the PR metadata produced by the Create or
update PR step, so calling workflows cannot access it cleanly. Add an outputs
block to the action and map through the create-pull-request step’s
pull-request-number and pull-request-url outputs from the pr step, keeping the
existing Enable auto-merge and Update branch to base logic unchanged.
.github/workflows/auto-label-ai-contribution.yml (1)

84-96: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Label is never removed once satisfying conditions are met.

Since on.types includes edited/synchronize, the workflow re-runs when the PR body/branch changes, but the script only calls addLabels — it never removes needs-maintainer if a later edit fills in the missing sections. This can leave stale labels around after the underlying concern is resolved.

♻️ Suggested addition
             if (!needsMaintainer) {
               core.info('AI contribution PR does not need maintainer triage.');
+              const hasLabel = (pr.labels || []).some(l => l.name === 'needs-maintainer');
+              if (hasLabel) {
+                await github.rest.issues.removeLabel({
+                  owner: context.repo.owner,
+                  repo: context.repo.repo,
+                  issue_number: pr.number,
+                  name: 'needs-maintainer',
+                });
+              }
               return;
             }
🤖 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/auto-label-ai-contribution.yml around lines 84 - 96, The
auto-label workflow only adds the needs-maintainer label in the PR handling
logic and never removes it when a later edited/synchronized PR now satisfies the
checks. Update the same label-management path that calls
github.rest.issues.addLabels so it also removes needs-maintainer when
needsMaintainer becomes false, using the existing pr/context.repo fields and the
label decision logic around needsMaintainer to keep labels in sync.
web/base.css (3)

79-85: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

currentColor casing flagged by stylelint.

🎨 Proposed fix
-a:hover { color: var(--vermillion-2); border-bottom-color: currentColor; }
+a:hover { color: var(--vermillion-2); border-bottom-color: currentcolor; }
🤖 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 `@web/base.css` around lines 79 - 85, The link hover rule in the global anchor
styles is using a `currentColor` value that stylelint flags for casing. Update
the `a:hover` declaration in the anchor styling block to use the exact
stylelint-preferred casing for that keyword, keeping the existing hover behavior
and related `border-bottom-color` rule unchanged.

Source: Linters/SAST tools


13-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keyword casing flagged by stylelint.

Minor casing lint findings on the new token block.

🎨 Proposed fix
-  --font-display: 'Archivo', 'Inter', system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;
+  --font-display: 'Archivo', 'Inter', system-ui, -apple-system, 'Segoe UI', helvetica, arial, sans-serif;
🤖 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 `@web/base.css` around lines 13 - 29, The new CSS custom property block in
:root has keyword casing lint issues, so normalize the quoted font-family
keyword casing to match the project’s stylelint rules. Update the font token
declarations in the :root token block consistently, especially the font stacks
used by --font-display, --font-body, and --font-mono, so the casing is uniform
and passes the keyword-case check.

Source: Linters/SAST tools


417-441: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Repeated hardcoded vermillion-alpha literals instead of a token.

rgba(255,90,61, X) (matching --vermillion) is duplicated across many rules (e.g. .rx-symbol background/border here, .feature:hover/.show-card:hover/.tile:hover borders, code background, a border-bottom, hero gradient). Since this file already centralizes color in custom properties, consider adding a couple of alpha-variant tokens (or color-mix()) to avoid re-deriving the same RGB triplet by hand if the brand color ever changes.

♻️ Example approach
 :root {
   --vermillion: `#ff5a3d`;
   --vermillion-2:`#ff7c60`;
+  --vermillion-a10: rgba(255,90,61,.1);
+  --vermillion-a35: rgba(255,90,61,.35);
+  --vermillion-a45: rgba(255,90,61,.45);
 }

Then replace occurrences like rgba(255,90,61,.1) / .35 / .45 with the corresponding variable throughout the file.

🤖 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 `@web/base.css` around lines 417 - 441, The stylesheet repeats the same
vermillion alpha values in multiple selectors, so the brand color is hardcoded
instead of centralized. Add reusable alpha-friendly tokens (or use color-mix())
near the existing custom properties, then update `.rx-symbol` and the other
hover/code/link/hero rules to consume those tokens instead of
`rgba(255,90,61,...)`. Keep the change anchored around the shared color
definitions so future `--vermillion` updates only need one place.
web/app.css (1)

134-182: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace deprecated word-break: break-word.

Stylelint flags this as a deprecated keyword; overflow-wrap: anywhere is the modern replacement for wrapping long unbroken command output in .terminal-body.

🎨 Proposed fix
 .terminal-body {
   padding: 18px 20px 22px;
   min-height: 280px;
   white-space: pre-wrap;
-  word-break: break-word;
+  overflow-wrap: anywhere;
 }
🤖 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 `@web/app.css` around lines 134 - 182, Update the .terminal-body styling to
replace the deprecated word-break: break-word usage with the modern wrapping
approach. Keep the existing long-output wrapping behavior for terminal content
by using overflow-wrap: anywhere in the .terminal-body rule, and adjust the
.terminal-body selector as needed to preserve the current pre-wrap layout and
readability.

Source: Linters/SAST tools

src/vouch/health.py (1)

46-56: 🚀 Performance & Scalability | 🔵 Trivial

Docstring overstates the efficiency; full file is still read into memory.

The docstring says "without parsing the whole log," but log.read_text().splitlines() still reads and materializes the entire file every call — only the per-line JSON parse is short-circuited. For a long-lived append-only audit log this becomes an O(file size) read on every status() call. Consider tailing the file (e.g., seek from the end and read a bounded chunk, growing the window if no valid line is found) instead of a full read.

🤖 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 46 - 56, The _last_audit_at helper in
health.py still reads the entire audit.log.jsonl into memory, so its docstring
is misleading and status() remains O(file size) per call. Update _last_audit_at
to avoid log.read_text().splitlines() by tailing the file from the end with a
bounded chunk and expanding only if needed until a valid JSON line is found.
Keep the docstring in sync with the actual behavior of _last_audit_at and
preserve the existing None-on-OSError behavior.
🤖 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/actions/create-auto-merge-pr/action.yml:
- Around line 39-51: The two post-PR steps in the auto-merge action handle
failures inconsistently: the "Enable auto-merge" step can fail the whole job
when `gh pr merge --auto` is blocked, while "Update branch to base" already
ignores non-critical failures. Update the `Enable auto-merge` step in the action
workflow so it tolerates expected non-zero exits the same way (or otherwise
guards execution) without failing the PR creation flow, and keep the logic
centered around the `steps.pr.outputs.pull-request-number` condition and the `gh
pr merge` command.

In @.github/workflows/auto-label-ai-contribution.yml:
- Around line 38-82: The AI triage logic in the workflow is checking for
headings that do not exist in the current PR template, so template-based AI PRs
are not being recognized. Update the matching in the workflow logic around
isAiContribution, sectionBody, and needsMaintainer to use the actual template
headings like What changed, Why, What might break, VEP, and Tests, or add the
missing AI-specific sections to the PR template so the regex checks align.
Ensure the branch-prefix check and the section-based checks are consistent with
the same template structure.

In `@adapters/claude-code/.claude/commands/vouch-start.md`:
- Around line 15-19: The fallback flow in the vouch-start command only handles
claim/page reads, so proposal refs still fail when Bash is unavailable. Update
the MCP fallback in the vouch-start command instructions to first recognize
proposal refs and query the proposal-aware MCP path before falling back to
kb_read_claim and kb_read_page. Use the existing vouch session start-from flow
as the primary reference and extend the fallback logic so proposal ids are
resolved consistently.

In `@adapters/openclaw/README.md`:
- Around line 6-14: Update the adapter README wording to match the current
bundle size: the paragraph referencing the OpenClaw plugin install flow still
says “four skills” even though the manifest now exposes five bundled commands.
Edit the description around openclaw.plugin.json, package.json, and
vouch-context-engine.mjs so it says five skills/commands, including
/vouch-start, and keep the rest of the install and context-engine explanation
unchanged.

In `@adapters/openclaw/skills/vouch-start/SKILL.md`:
- Around line 15-19: The fallback in vouch-start only handles claim/page lookup,
so proposal refs are not resolved when the CLI is unavailable. Update the MCP
fallback in SKILL.md for the vouch-start flow to detect proposal-style refs and
query the proposal-reading MCP path before falling back to kb_read_claim and
kb_read_page. Keep the existing vouch session start-from CLI behavior, and
ensure the fallback uses the same ref input so /vouch-start can resolve proposal
ids as well as claims and pages.

In `@adapters/openclaw/skills/vouch-status/SKILL.md`:
- Around line 14-21: The fenced example in SKILL.md is missing a language tag
and triggers markdownlint md040. Update the example fence in the status block to
use a non-rendering language tag such as text so the output stays the same;
locate the fenced block by its KB status snippet and adjust the opening fence
only.

In `@docs/superpowers/specs/2026-07-05-vouch-cloud-design.md`:
- Around line 77-97: The approve/reject actions are still callable even if they
are hidden from the advertised tool list, because dispatch in jsonl_server and
tool registration in server still expose them. Enforce authorization before
method execution by gating kb.approve/kb.reject in the jsonl_server HANDLERS
dispatch path and in the vouch.server tool registration flow, or move those
handlers behind a trusted-only surface. Reference the approve/reject entry
points and the FastMCP/jsonl RPC surfaces so read+propose tokens cannot invoke
them.

In `@README.md`:
- Line 14: The header badge in the README has a handle mismatch between the link
target and the displayed badge label. Update the anchor and badge metadata
together so the href, visible text, and alt text all reference the same X handle
in the README header badge markup.

In `@src/vouch/summarize.py`:
- Around line 272-288: The session lookup in find_transcript still allows glob
metacharacters in session_id, so the glob can match unrelated transcripts and
return the wrong first hit. Update the sanitization in find_transcript to escape
the session identifier before building the projects.glob pattern, keeping the
existing slash/dotdot guard but ensuring wildcard characters are treated
literally.

In `@web/app.js`:
- Around line 410-430: The initCopy click handler can leave the button stuck on
"copied" because repeated clicks overwrite the saved label and create
overlapping untracked timers. Update the initCopy logic so each button tracks
its own original label and timeout state across clicks, clears or replaces any
existing reset timer before starting a new one, and restores the label reliably
after navigator.clipboard.writeText completes. Also add a catch/finally path in
the click handler to handle clipboard write failures and ensure the button state
is reset even when the promise rejects.

---

Outside diff comments:
In `@tests/test_merge_pending.py`:
- Around line 1-188: Add JSONL request coverage for the kb.merge_pending
endpoint exposed by jsonl_server.register_kb_methods, since the current tests
only cover the underlying proposal merge logic. In tests/test_jsonl_server.py,
add one request asserting the success envelope returned by kb.merge_pending
includes id, ok, and a result payload, and another asserting the failure path
returns id, ok: false, and an error object. Use the existing JSONL request
helper and the kb.merge_pending method name to locate the relevant test setup.

---

Nitpick comments:
In @.github/actions/create-auto-merge-pr/action.yml:
- Around line 24-51: The composite action currently hides the PR metadata
produced by the Create or update PR step, so calling workflows cannot access it
cleanly. Add an outputs block to the action and map through the
create-pull-request step’s pull-request-number and pull-request-url outputs from
the pr step, keeping the existing Enable auto-merge and Update branch to base
logic unchanged.

In @.github/workflows/auto-label-ai-contribution.yml:
- Around line 84-96: The auto-label workflow only adds the needs-maintainer
label in the PR handling logic and never removes it when a later
edited/synchronized PR now satisfies the checks. Update the same
label-management path that calls github.rest.issues.addLabels so it also removes
needs-maintainer when needsMaintainer becomes false, using the existing
pr/context.repo fields and the label decision logic around needsMaintainer to
keep labels in sync.

In `@docs/superpowers/specs/2026-07-04-decision-distill-design.md`:
- Around line 187-196: The config namespace is being introduced inconsistently:
this spec adds `distill.*` while the shipped code already uses
`capture.summary_*` in `storage.py` and `summarize.py`. Update the spec to use
the existing namespace, or explicitly mark `distill.*` as future-only and
document the migration path; reference the `_starter_config` suggestion and the
`capture.load_config` template so the naming stays singular across the
deployment contract.

In `@src/vouch/health.py`:
- Around line 46-56: The _last_audit_at helper in health.py still reads the
entire audit.log.jsonl into memory, so its docstring is misleading and status()
remains O(file size) per call. Update _last_audit_at to avoid
log.read_text().splitlines() by tailing the file from the end with a bounded
chunk and expanding only if needed until a valid JSON line is found. Keep the
docstring in sync with the actual behavior of _last_audit_at and preserve the
existing None-on-OSError behavior.

In `@src/vouch/summarize.py`:
- Around line 176-198: The subprocess call in the summarizer path can leave
orphaned children when `config.llm_cmd` times out because `subprocess.run` with
`shell=True` only terminates the shell wrapper. Update the `subprocess.run`
invocation in the summarization helper to start a new process group/session, and
add timeout handling that explicitly kills the whole group before returning None
so repeated `vouch summarize` or `SessionEnd` runs do not leak `claude -p`
descendants.

In `@web/app.css`:
- Around line 134-182: Update the .terminal-body styling to replace the
deprecated word-break: break-word usage with the modern wrapping approach. Keep
the existing long-output wrapping behavior for terminal content by using
overflow-wrap: anywhere in the .terminal-body rule, and adjust the
.terminal-body selector as needed to preserve the current pre-wrap layout and
readability.

In `@web/base.css`:
- Around line 79-85: The link hover rule in the global anchor styles is using a
`currentColor` value that stylelint flags for casing. Update the `a:hover`
declaration in the anchor styling block to use the exact stylelint-preferred
casing for that keyword, keeping the existing hover behavior and related
`border-bottom-color` rule unchanged.
- Around line 13-29: The new CSS custom property block in :root has keyword
casing lint issues, so normalize the quoted font-family keyword casing to match
the project’s stylelint rules. Update the font token declarations in the :root
token block consistently, especially the font stacks used by --font-display,
--font-body, and --font-mono, so the casing is uniform and passes the
keyword-case check.
- Around line 417-441: The stylesheet repeats the same vermillion alpha values
in multiple selectors, so the brand color is hardcoded instead of centralized.
Add reusable alpha-friendly tokens (or use color-mix()) near the existing custom
properties, then update `.rx-symbol` and the other hover/code/link/hero rules to
consume those tokens instead of `rgba(255,90,61,...)`. Keep the change anchored
around the shared color definitions so future `--vermillion` updates only need
one place.

In `@web/index.html`:
- Around line 248-252: The Install paragraph in this page is using a smaller
font size than the matching paragraph on the sibling pages, creating an
unintended style drift. Update the inline styling on the Install section’s
paragraph in the relevant HTML block so it matches the same font-size used by
the equivalent footer paragraph on how-it-works.html, reference.html, and
gittensor.html, keeping the rest of the markup unchanged.
🪄 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: 426059cb-629e-44c1-aa6d-af171281f606

📥 Commits

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

⛔ Files ignored due to path filters (7)
  • docs/img/examples/decision-log-diff.svg is excluded by !**/*.svg
  • docs/img/examples/decision-log-search.svg is excluded by !**/*.svg
  • docs/img/vouch-auto-capture-1270x760.png is excluded by !**/*.png
  • docs/img/vouch-plaintext-cited-auditable-1270x760.png is excluded by !**/*.png
  • docs/img/vouch-review-gate-1270x760.png is excluded by !**/*.png
  • docs/img/vouch-ui-review-deployed.png is excluded by !**/*.png
  • docs/presentation/vouch-claude-code-live.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (103)
  • .claude/settings.json
  • .github/actions/create-auto-merge-pr/action.yml
  • .github/workflows/auto-label-ai-contribution.yml
  • .gitignore
  • .vouch/audit.log.jsonl
  • .vouch/claims/vouch-uses-a-review-gated-proposal-workflow-agents-propose-c.yaml
  • .vouch/config.yaml
  • .vouch/decided/20260521-055206-7d6d92d6.yaml
  • .vouch/sources/06d8519f8dcf4149d23c8a48984541b2e9365ec364e7e58192e28ed149a2c47c/content
  • .vouch/sources/06d8519f8dcf4149d23c8a48984541b2e9365ec364e7e58192e28ed149a2c47c/meta.yaml
  • .vouch/sources/67478e72acfb8fac3a059143e95c95f5cc6f7e8d4dccc05fbcea8dbccb8a4eba/content
  • .vouch/sources/67478e72acfb8fac3a059143e95c95f5cc6f7e8d4dccc05fbcea8dbccb8a4eba/meta.yaml
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • adapters/claude-code/.claude/commands/vouch-recall.md
  • adapters/claude-code/.claude/commands/vouch-resolve-issue.md
  • adapters/claude-code/.claude/commands/vouch-start.md
  • adapters/claude-code/.claude/commands/vouch-status.md
  • adapters/claude-code/.claude/settings.json
  • adapters/claude-code/install.yaml
  • adapters/openclaw/AGENTS.md.snippet
  • adapters/openclaw/README.md
  • adapters/openclaw/install.yaml
  • adapters/openclaw/skills/vouch-propose-from-pr/SKILL.md
  • adapters/openclaw/skills/vouch-recall/SKILL.md
  • adapters/openclaw/skills/vouch-resolve-issue/SKILL.md
  • adapters/openclaw/skills/vouch-start/SKILL.md
  • adapters/openclaw/skills/vouch-status/SKILL.md
  • adapters/openclaw/vouch-context-engine.mjs
  • docs/getting-started.md
  • docs/presentation/vouch-claude-code-live.html
  • docs/superpowers/specs/2026-07-04-company-brain-design.md
  • docs/superpowers/specs/2026-07-04-decision-distill-design.md
  • docs/superpowers/specs/2026-07-05-vouch-cloud-design.md
  • docs/tutorials/README.md
  • docs/tutorials/remember-across-sessions.md
  • examples/decision-log/vouch/.gitignore
  • examples/decision-log/vouch/audit.log.jsonl
  • examples/decision-log/vouch/claims/billing-data-requires-acid-guarantees-postgresql-is-the-prim.yaml
  • examples/decision-log/vouch/claims/critical-system-incidents-must-have-a-15-minute-response-sla.yaml
  • examples/decision-log/vouch/claims/free-tier-100-req-superseded.yaml
  • examples/decision-log/vouch/claims/free-tier-500-req.yaml
  • examples/decision-log/vouch/claims/free-tier-gets-100-requests-per-day-as-baseline.yaml
  • examples/decision-log/vouch/claims/incident-response-15min-sla.yaml
  • examples/decision-log/vouch/claims/use-postgres-for-billing.yaml
  • examples/decision-log/vouch/claims/use-postgresql-15-for-enhanced-replication-and-monitoring-fe.yaml
  • examples/decision-log/vouch/claims/vouch-starter-reviewed-knowledge.yaml
  • examples/decision-log/vouch/config.yaml
  • examples/decision-log/vouch/decided/20260703-091452-063145f6.yaml
  • examples/decision-log/vouch/decided/20260703-091452-4ffb9687.yaml
  • examples/decision-log/vouch/decided/20260703-091452-976471f3.yaml
  • examples/decision-log/vouch/decided/20260703-091453-4ca258b5.yaml
  • examples/decision-log/vouch/pages/edit-in-obsidian.md
  • examples/decision-log/vouch/schema_version
  • examples/decision-log/vouch/sources/1f8ef21996a0f59eeb5174071f5293fc3b864defe928b9ce7879022a8230dbc8/content
  • examples/decision-log/vouch/sources/1f8ef21996a0f59eeb5174071f5293fc3b864defe928b9ce7879022a8230dbc8/meta.yaml
  • examples/decision-log/vouch/sources/3131367abbcefab105fa2fcaa36617bfdda15511cc8880145af113731e95ad6d/content
  • examples/decision-log/vouch/sources/3131367abbcefab105fa2fcaa36617bfdda15511cc8880145af113731e95ad6d/meta.yaml
  • examples/decision-log/vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/content
  • examples/decision-log/vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/meta.yaml
  • examples/jsonl-quickstart/run.sh
  • openclaw.plugin.json
  • package.json
  • pyproject.toml
  • src/vouch/capabilities.py
  • src/vouch/capture.py
  • src/vouch/cli.py
  • src/vouch/health.py
  • src/vouch/jsonl_server.py
  • src/vouch/models.py
  • src/vouch/openclaw/context_engine.py
  • src/vouch/openclaw/rpc.py
  • src/vouch/proposals.py
  • src/vouch/server.py
  • src/vouch/sessions.py
  • src/vouch/storage.py
  • src/vouch/summarize.py
  • src/vouch/web/server.py
  • src/vouch/web/static/app.css
  • src/vouch/web/static/app.js
  • src/vouch/web/templates/base.html
  • src/vouch/web/templates/claim.html
  • src/vouch/web/templates/queue.html
  • src/vouch/web/templates/session.html
  • src/vouch/web/templates/source.html
  • src/vouch/web/templates/start_from.html
  • tests/test_capture.py
  • tests/test_install_adapter.py
  • tests/test_merge_pending.py
  • tests/test_openclaw_context_engine.py
  • tests/test_openclaw_plugin_load_real.py
  • tests/test_openclaw_plugin_manifest.py
  • tests/test_sessions.py
  • tests/test_summarize.py
  • tests/test_web.py
  • web/app.css
  • web/app.js
  • web/base.css
  • web/gittensor.html
  • web/how-it-works.html
  • web/index.html
  • web/reference.html
💤 Files with no reviewable changes (12)
  • examples/decision-log/vouch/claims/use-postgres-for-billing.yaml
  • examples/decision-log/vouch/claims/free-tier-500-req.yaml
  • .vouch/audit.log.jsonl
  • examples/decision-log/vouch/claims/incident-response-15min-sla.yaml
  • .vouch/sources/67478e72acfb8fac3a059143e95c95f5cc6f7e8d4dccc05fbcea8dbccb8a4eba/meta.yaml
  • .vouch/sources/06d8519f8dcf4149d23c8a48984541b2e9365ec364e7e58192e28ed149a2c47c/content
  • .vouch/sources/06d8519f8dcf4149d23c8a48984541b2e9365ec364e7e58192e28ed149a2c47c/meta.yaml
  • .vouch/decided/20260521-055206-7d6d92d6.yaml
  • examples/decision-log/vouch/claims/free-tier-100-req-superseded.yaml
  • .vouch/config.yaml
  • .vouch/claims/vouch-uses-a-review-gated-proposal-workflow-agents-propose-c.yaml
  • .vouch/sources/67478e72acfb8fac3a059143e95c95f5cc6f7e8d4dccc05fbcea8dbccb8a4eba/content

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

🛑 Comments failed to post (11)
.github/actions/create-auto-merge-pr/action.yml (1)

39-51: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Inconsistent error handling between the two post-PR steps.

"Update branch to base" tolerates failure with || true, but "Enable auto-merge" has no fallback. If the repo doesn't have "Allow auto-merge" enabled, branch protection blocks it, or the actor lacks permission, gh pr merge --auto exits non-zero and fails the whole job even though the PR was created successfully.

🔧 Proposed fix
     - name: Enable auto-merge
       if: steps.pr.outputs.pull-request-number
       shell: bash
-      run: gh pr merge ${{ steps.pr.outputs.pull-request-number }} --squash --auto
+      run: gh pr merge ${{ steps.pr.outputs.pull-request-number }} --squash --auto || true
       env:
         GH_TOKEN: ${{ inputs.token }}
📝 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.

    - name: Enable auto-merge
      if: steps.pr.outputs.pull-request-number
      shell: bash
      run: gh pr merge ${{ steps.pr.outputs.pull-request-number }} --squash --auto || true
      env:
        GH_TOKEN: ${{ inputs.token }}

    - name: Update branch to base
      if: steps.pr.outputs.pull-request-number
      shell: bash
      run: gh pr update-branch ${{ steps.pr.outputs.pull-request-number }} || true
      env:
        GH_TOKEN: ${{ inputs.token }}
🤖 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/actions/create-auto-merge-pr/action.yml around lines 39 - 51, The
two post-PR steps in the auto-merge action handle failures inconsistently: the
"Enable auto-merge" step can fail the whole job when `gh pr merge --auto` is
blocked, while "Update branch to base" already ignores non-critical failures.
Update the `Enable auto-merge` step in the action workflow so it tolerates
expected non-zero exits the same way (or otherwise guards execution) without
failing the PR creation flow, and keep the logic centered around the
`steps.pr.outputs.pull-request-number` condition and the `gh pr merge` command.
.github/workflows/auto-label-ai-contribution.yml (1)

38-82: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for any additional PR templates with Track/Verification sections
fd -HI 'PULL_REQUEST_TEMPLATE' .github
rg -n '##\s+(Track|Verification|Scope Expansion|Validation Failures|CI Failures)' .github

Repository: vouchdev/vouch

Length of output: 316


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Template files =="
fd -H -I 'PULL_REQUEST_TEMPLATE*' .github

echo
echo "== PR template contents =="
wc -l .github/PULL_REQUEST_TEMPLATE.md
cat -n .github/PULL_REQUEST_TEMPLATE.md

echo
echo "== Workflow excerpt =="
sed -n '38,86p' .github/workflows/auto-label-ai-contribution.yml

Repository: vouchdev/vouch

Length of output: 3594


Align the AI triage checks with the actual PR template. .github/PULL_REQUEST_TEMPLATE.md only has What changed, Why, What might break, VEP, and Tests; there is no AI contributor, Track, Verification, Scope Expansion, Validation Failures, or CI Failures section. As written, template-based AI PRs won’t match hasTemplateSignature, and needsMaintainer will stay false unless the branch is ai/ or bot/. Either key this off the existing headings or add the AI-specific template these regexes expect.

🤖 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/auto-label-ai-contribution.yml around lines 38 - 82, The
AI triage logic in the workflow is checking for headings that do not exist in
the current PR template, so template-based AI PRs are not being recognized.
Update the matching in the workflow logic around isAiContribution, sectionBody,
and needsMaintainer to use the actual template headings like What changed, Why,
What might break, VEP, and Tests, or add the missing AI-specific sections to the
PR template so the regex checks align. Ensure the branch-prefix check and the
section-based checks are consistent with the same template structure.
adapters/claude-code/.claude/commands/vouch-start.md (1)

15-19: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n '\bkb_read_(claim|page|proposal)\b' src adapters tests

Repository: vouchdev/vouch

Length of output: 612


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant command bodies =="
sed -n '1,120p' adapters/claude-code/.claude/commands/vouch-start.md
printf '\n---\n'
sed -n '1,120p' adapters/openclaw/skills/vouch-start/SKILL.md

echo "\n== server read handlers and proposal references =="
sed -n '240,340p' src/vouch/server.py
printf '\n---\n'
rg -n '\bproposal\b|proposal_id|approved claim|approved page|kb_read_' src/vouch adapters/openclaw/skills -n

Repository: vouchdev/vouch

Length of output: 46329


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== adapters/claude-code/.claude/commands/vouch-start.md =="
nl -ba adapters/claude-code/.claude/commands/vouch-start.md | sed -n '1,120p'

echo "\n== adapters/openclaw/skills/vouch-start/SKILL.md =="
nl -ba adapters/openclaw/skills/vouch-start/SKILL.md | sed -n '1,120p'

echo "\n== src/vouch/server.py read handlers =="
nl -ba src/vouch/server.py | sed -n '250,330p'

echo "\n== proposal references in repo =="
rg -n '\bproposal\b|proposal_id|approved claim|approved page|kb_read_' src adapters/openclaw/skills -n

Repository: vouchdev/vouch

Length of output: 251


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== any proposal read implementation or routing =="
rg -n '\bproposal\b' src/vouch adapters/openclaw/skills | sed -n '1,200p'

echo "\n== exact signatures for read handlers =="
python3 - <<'PY'
from pathlib import Path
import re

text = Path('src/vouch/server.py').read_text()
for name in ('kb_read_claim', 'kb_read_page'):
    m = re.search(rf'def {name}\(([^)]*)\) -> ([^\n:]+):', text)
    print(name, '(', m.group(1), ') ->', m.group(2) if m else 'NOT FOUND')
PY

Repository: vouchdev/vouch

Length of output: 17817


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '130,210p' src/vouch/sessions.py

Repository: vouchdev/vouch

Length of output: 3450


Add a proposal-aware MCP fallback The CLI accepts proposal refs here, but the fallback only tries kb_read_claim and kb_read_page, so proposal ids still fail when Bash isn’t available.

🤖 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 `@adapters/claude-code/.claude/commands/vouch-start.md` around lines 15 - 19,
The fallback flow in the vouch-start command only handles claim/page reads, so
proposal refs still fail when Bash is unavailable. Update the MCP fallback in
the vouch-start command instructions to first recognize proposal refs and query
the proposal-aware MCP path before falling back to kb_read_claim and
kb_read_page. Use the existing vouch session start-from flow as the primary
reference and extend the fallback logic so proposal ids are resolved
consistently.
adapters/openclaw/README.md (1)

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

update the count to five.

this paragraph still says “four skills” even though the adapter now ships five bundled commands, including /vouch-start. please align the wording with the updated manifest and install docs.

proposed fix
-   publishes the four skills under [`skills/`](./skills/) as slash commands.
+   publishes the five skills under [`skills/`](./skills/) as slash commands.
📝 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.

   [`openclaw.plugin.json`](../../openclaw.plugin.json) manifest plus
   [`package.json`](../../package.json) (the loader-facing
   `openclaw.extensions` pointer at
   [`vouch-context-engine.mjs`](./vouch-context-engine.mjs)) —
   `openclaw plugins install --link <repo>` registers the context engine
   (auto-bound to `plugins.slots.contextEngine`) and publishes the five
   skills under [`skills/`](./skills/) as slash commands. The kb.* MCP
   server is deployment config: `openclaw mcp add vouch -- vouch serve`.
   See the README section "Running vouch as an OpenClaw plugin".
🤖 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 `@adapters/openclaw/README.md` around lines 6 - 14, Update the adapter README
wording to match the current bundle size: the paragraph referencing the OpenClaw
plugin install flow still says “four skills” even though the manifest now
exposes five bundled commands. Edit the description around openclaw.plugin.json,
package.json, and vouch-context-engine.mjs so it says five skills/commands,
including /vouch-start, and keep the rest of the install and context-engine
explanation unchanged.
adapters/openclaw/skills/vouch-start/SKILL.md (1)

15-19: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n '\bkb_read_(claim|page|proposal)\b' src adapters tests

Repository: vouchdev/vouch

Length of output: 612


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- relevant file map ---'
git ls-files 'adapters/openclaw/skills/vouch-start/SKILL.md' 'adapters/claude-code/.claude/commands/vouch-start.md' 'src/vouch/server.py' 'src/vouch/recall.py'

echo
echo '--- skill/command body comparison ---'
python3 - <<'PY'
from pathlib import Path

paths = [
    Path('adapters/openclaw/skills/vouch-start/SKILL.md'),
    Path('adapters/claude-code/.claude/commands/vouch-start.md'),
]
for p in paths:
    print(f'\n## {p}')
    print(p.read_text())
PY

echo
echo '--- proposal / claim / page references ---'
rg -n '\bproposal\b|\bclaim\b|\bpage\b|kb_read_(claim|page|proposal)' src/vouch adapters/openclaw/skills/vouch-start adapters/claude-code/.claude/commands/vouch-start.md

echo
echo '--- server.py focused slice ---'
sed -n '250,340p' src/vouch/server.py

Repository: vouchdev/vouch

Length of output: 50370


Add a proposal-aware MCP fallback adapters/openclaw/skills/vouch-start/SKILL.md:15-19

The CLI path accepts proposal ids, but the fallback only tries kb_read_claim and kb_read_page. There’s no proposal read path here, so /vouch-start won’t resolve proposal refs when the CLI is unavailable.

🤖 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 `@adapters/openclaw/skills/vouch-start/SKILL.md` around lines 15 - 19, The
fallback in vouch-start only handles claim/page lookup, so proposal refs are not
resolved when the CLI is unavailable. Update the MCP fallback in SKILL.md for
the vouch-start flow to detect proposal-style refs and query the
proposal-reading MCP path before falling back to kb_read_claim and kb_read_page.
Keep the existing vouch session start-from CLI behavior, and ensure the fallback
uses the same ref input so /vouch-start can resolve proposal ids as well as
claims and pages.
adapters/openclaw/skills/vouch-status/SKILL.md (1)

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

add a language tag to the example fence.

markdownlint will flag this block as md040. text keeps the rendered output unchanged.

♻️ proposed fix
-```
+```text
📝 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.


🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 14-14: 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 `@adapters/openclaw/skills/vouch-status/SKILL.md` around lines 14 - 21, The
fenced example in SKILL.md is missing a language tag and triggers markdownlint
md040. Update the example fence in the status block to use a non-rendering
language tag such as text so the output stays the same; locate the fenced block
by its KB status snippet and adjust the opening fence only.

Source: Linters/SAST tools

docs/superpowers/specs/2026-07-05-vouch-cloud-design.md (1)

77-97: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Identify the spec and implementation files related to the claim.
git ls-files | rg '(^|/)(docs/superpowers/specs/2026-07-05-vouch-cloud-design\.md|src/vouch/web/server\.py|.*mcp.*|.*jsonl.*|.*rpc.*|.*FastMCP.*|.*tool.*)$'

# Show the relevant section of the spec with line numbers.
spec='docs/superpowers/specs/2026-07-05-vouch-cloud-design.md'
if [ -f "$spec" ]; then
  sed -n '70,110p' "$spec" | cat -n
fi

# Search for approve/reject tool registration, dispatcher logic, and auth checks.
rg -n -C 3 'kb_approve|kb_reject|approve|reject|dispatch|tool registry|jsonl_server|FastMCP|streamable-http|contextvar|context var|auth|authorization|scope' src docs -g '!**/.git/**'

# If there are candidate MCP/jsonl files, map their structure before reading slices.
for f in $(git ls-files | rg '(mcp|jsonl|rpc|FastMCP|tool)'); do
  echo "### OUTLINE: $f"
  ast-grep outline "$f" --view expanded || true
done

Repository: vouchdev/vouch

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find likely implementation files without touching generated/vendor assets.
git ls-files | rg '(^|/)(jsonl_server|mcp|rpc|fastmcp|tool_registry|tools|server)\.(py|ts|js)$|(^|/)server\.py$'

# Show concise structure for the most likely Python files.
for f in $(git ls-files | rg '(^|/)(jsonl_server|mcp|rpc|fastmcp|tool_registry|tools|server)\.py$'); do
  echo "### $f"
  ast-grep outline "$f" --view expanded || true
done

# Search only likely source directories for the specific claim.
rg -n -C 2 'kb_approve|kb_reject|approve|reject|dispatch|dispatcher|tool(s)? registry|FastMCP|streamable-http|jsonl_server|rpc|bearer token|contextvar|authorization|authz|scope' src/vouch src -g '!**/static/vendor/**' -g '!**/node_modules/**'

# If a JSONL/MCP file is found, inspect the relevant slice only.
for f in $(git ls-files | rg '(^|/)(jsonl_server|mcp|rpc|fastmcp|tool_registry|tools)\.py$'); do
  if [ -f "$f" ]; then
    echo "### FILE: $f"
    sed -n '1,260p' "$f" | cat -n | sed -n '1,220p'
  fi
done

Repository: vouchdev/vouch

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the JSONL request handler and any method-to-tool dispatch.
f='src/vouch/jsonl_server.py'
if [ -f "$f" ]; then
  echo "### $f"
  ast-grep outline "$f" --view expanded || true
  sed -n '1,260p' "$f" | cat -n | sed -n '1,260p'
fi

# Inspect the tool definitions for approve/reject and any auth/scope gating.
for f in $(git ls-files | rg 'src/vouch/.*\.(py|ts|js)$'); do
  if rg -n 'def (kb_approve|kb_reject)|async def (kb_approve|kb_reject)|@.*tool|approve\(|reject\(' "$f" >/dev/null 2>&1; then
    echo "### MATCHES IN $f"
    rg -n -C 3 'def (kb_approve|kb_reject)|async def (kb_approve|kb_reject)|@.*tool|approve\(|reject\(' "$f"
  fi
done

# Read the HTTP/MCP mounting code around the trust/auth boundary only.
sed -n '175,360p' src/vouch/http_server.py | cat -n

Repository: vouchdev/vouch

Length of output: 43664


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the dispatch function and the handler table in compact slices.
sed -n '750,860p' src/vouch/jsonl_server.py | cat -n

# Read the trust model and any request-scope helpers that might gate tools.
sed -n '1,260p' src/vouch/trust.py | cat -n

# Inspect the MCP tool exposure path for any filtering by trust/scope.
sed -n '1,220p' src/vouch/server.py | cat -n

Repository: vouchdev/vouch

Length of output: 21295


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any capability/scope model for read+propose vs approve/reject.
rg -n -C 3 'read\+propose|propose.*approve|approve.*propose|kb_approve|kb_reject|tool list|capabilit(y|ies)|scope|role|permission|token' docs src/vouch -g '!**/static/vendor/**'

# Inspect capability generation if it exists.
sed -n '1,220p' src/vouch/capabilities.py | cat -n

# Inspect any tests or docs that describe tool subsets by token/scope.
rg -n -C 2 'read\+propose|approve/reject|kb\.approve|kb\.reject|read-only|propose-only|trusted-agent' tests docs src/vouch -g '!**/static/vendor/**'

Repository: vouchdev/vouch

Length of output: 50373


Enforce authz on approve/reject calls. Hiding kb.approve/kb.reject from the advertised tool list doesn’t stop invocation: src/vouch/jsonl_server.py dispatches any method present in HANDLERS, and src/vouch/server.py still registers both tools. If read+propose tokens are meant to exist, block these methods before dispatch or move them to a trusted-only surface.

🧰 Tools
🪛 LanguageTool

[grammar] ~95-~95: Ensure spelling is correct
Context: ...t resolved from the bearer token into a contextvar so the existing kb_* tools get the r...

(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 `@docs/superpowers/specs/2026-07-05-vouch-cloud-design.md` around lines 77 -
97, The approve/reject actions are still callable even if they are hidden from
the advertised tool list, because dispatch in jsonl_server and tool registration
in server still expose them. Enforce authorization before method execution by
gating kb.approve/kb.reject in the jsonl_server HANDLERS dispatch path and in
the vouch.server tool registration flow, or move those handlers behind a
trusted-only surface. Reference the approve/reject entry points and the
FastMCP/jsonl RPC surfaces so read+propose tokens cannot invoke them.
README.md (1)

14-14: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the badge link and label use the same handle.

The new header badge points to vouch_dev, but the badge text/alt use vouch__dev. That sends readers to the wrong profile.

♻️ Proposed fix
-  <a href="https://x.com/vouch_dev"><img src="https://img.shields.io/badge/follow-%40vouch__dev-000000?logo=x&logoColor=white" alt="Follow `@vouch_dev` on X"></a>
+  <a href="https://x.com/vouch__dev"><img src="https://img.shields.io/badge/follow-%40vouch__dev-000000?logo=x&logoColor=white" alt="Follow `@vouch__dev` on X"></a>
📝 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.

  <a href="https://x.com/vouch__dev"><img src="https://img.shields.io/badge/follow-%40vouch__dev-000000?logo=x&logoColor=white" alt="Follow `@vouch__dev` on X"></a>
🤖 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 `@README.md` at line 14, The header badge in the README has a handle mismatch
between the link target and the displayed badge label. Update the anchor and
badge metadata together so the href, visible text, and alt text all reference
the same X handle in the README header badge markup.
src/vouch/jsonl_server.py (1)

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'kb\.(list_sessions|summarize_session|merge_pending)' tests/test_jsonl_server.py

Repository: vouchdev/vouch

Length of output: 152


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- tests files ---\n'
git ls-files 'tests/*.py' | sort

printf '\n--- jsonl server references ---\n'
rg -n 'list_sessions|summarize_session|merge_pending|HANDLERS|_h_' src/vouch/jsonl_server.py

printf '\n--- test references for JSONL envelope ---\n'
rg -n '"id"|ok: false|error|result|jsonl' tests -g '*.py'

Repository: vouchdev/vouch

Length of output: 243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- tests files ---'
git ls-files 'tests/*.py' | sort

echo
echo '--- jsonl server references ---'
rg -n 'list_sessions|summarize_session|merge_pending|HANDLERS|_h_' src/vouch/jsonl_server.py

echo
echo '--- test references for JSONL envelope ---'
rg -n '"id"|ok: false|error|result|jsonl' tests -g '*.py'

Repository: vouchdev/vouch

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- direct references to the three methods in tests ---'
rg -n 'kb\.(list_sessions|summarize_session|merge_pending)|_h_(list_sessions|summarize_session|merge_pending)|handle_request\(' tests -g '*.py'

echo
echo '--- tests/test_jsonl_server.py around envelope assertions ---'
wc -l tests/test_jsonl_server.py
sed -n '1,260p' tests/test_jsonl_server.py

echo
echo '--- likely related tests files ---'
for f in tests/test_sessions.py tests/test_summarize.py tests/test_merge_pending.py; do
  if [ -f "$f" ]; then
    echo "### $f"
    rg -n 'kb\.(list_sessions|summarize_session|merge_pending)|handle_request\(|"id"|ok|error|result' "$f"
  fi
done

Repository: vouchdev/vouch

Length of output: 16594


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- direct references to the three methods in tests ---'
rg -n 'kb\.(list_sessions|summarize_session|merge_pending)|_h_(list_sessions|summarize_session|merge_pending)|handle_request\(' tests -g '*.py' | head -n 120

echo
echo '--- tests/test_jsonl_server.py around envelope assertions ---'
wc -l tests/test_jsonl_server.py
sed -n '1,260p' tests/test_jsonl_server.py

echo
echo '--- likely related tests files ---'
for f in tests/test_sessions.py tests/test_summarize.py tests/test_merge_pending.py; do
  if [ -f "$f" ]; then
    echo "### $f"
    rg -n 'kb\.(list_sessions|summarize_session|merge_pending)|handle_request\(|"id"|ok|error|result' "$f"
  fi
done

Repository: vouchdev/vouch

Length of output: 16594


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in tests/test_jsonl_server.py tests/test_sessions.py tests/test_summarize.py tests/test_merge_pending.py; do
  if [ -f "$f" ]; then
    echo "### $f"
    rg -n 'kb\.(list_sessions|summarize_session|merge_pending)|handle_request\(|"id"|ok|error|result|envelope' "$f"
    echo
  fi
done

Repository: vouchdev/vouch

Length of output: 6359


Add JSONL envelope tests for the new methods. tests/test_jsonl_server.py covers other handlers, but not kb.list_sessions, kb.summarize_session, or kb.merge_pending; add success and failure cases for the {id, ok, result} / {id, ok: false, error} envelope.

Source: Path instructions

src/vouch/summarize.py (1)

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- src/vouch/summarize.py (outline) ---'
ast-grep outline src/vouch/summarize.py --view expanded || true

printf '\n%s\n' '--- relevant definitions/usages ---'
rg -n "def find_transcript|kb\.summarize_session|--session|session_id|glob\(" src/vouch -S

printf '\n%s\n' '--- read target section ---'
sed -n '250,310p' src/vouch/summarize.py

printf '\n%s\n' '--- glob metacharacter probe ---'
python3 - <<'PY'
from pathlib import Path
import tempfile, os, shutil

root = Path(tempfile.mkdtemp())
try:
    projects = root / ".claude" / "projects"
    (projects / "projA").mkdir(parents=True)
    (projects / "projB").mkdir(parents=True)
    for p in [
        projects / "projA" / "abc.jsonl",
        projects / "projA" / "a1.jsonl",
        projects / "projB" / "zzz.jsonl",
    ]:
        p.write_text("x")

    for sess in ["abc", "*", "a?", "a[bc]"]:
        hits = sorted(projects.glob(f"*/{sess}.jsonl"))
        print(sess, "->", [str(h.relative_to(root)) for h in hits])
finally:
    shutil.rmtree(root)
PY

Repository: vouchdev/vouch

Length of output: 26527


Escape session_id before globbing. safe still allows *, ?, and [...], so projects.glob(f"*/{safe}.jsonl") can widen to unrelated transcripts and quietly pick the first sorted match. glob.escape fixes this with a tiny change.

🤖 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/summarize.py` around lines 272 - 288, The session lookup in
find_transcript still allows glob metacharacters in session_id, so the glob can
match unrelated transcripts and return the wrong first hit. Update the
sanitization in find_transcript to escape the session identifier before building
the projects.glob pattern, keeping the existing slash/dotdot guard but ensuring
wildcard characters are treated literally.
web/app.js (1)

410-430: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Rapid clicks can leave the copy button permanently stuck on "copied".

was is read from btn.textContent at click time, and each click starts a new, untracked setTimeout. If a second click fires before the first timeout resolves, was captures "copied" instead of the original label; when the timers eventually resolve out of order, the button can be left showing "copied" indefinitely. There's also no .catch() on the clipboard write promise, so a rejected write (e.g. permission denied) fails silently with no timer to reset any state.

🐛 Proposed fix
   function initCopy() {
     $all("[data-copy]").forEach(function (btn) {
+      var resetTimer;
       btn.addEventListener("click", function () {
         var src = $(btn.getAttribute("data-copy"));
         if (!src || !navigator.clipboard) return;
         var text = $all(".cmd", src)
           .map(function (line) {
             var dim = $(".dim", line);
             var t = line.textContent.replace(/^\$\s*/, "");
             if (dim) t = t.replace(dim.textContent, "");
             return t.trim();
           })
           .join("\n");
         navigator.clipboard.writeText(text || src.textContent.trim()).then(function () {
-          var was = btn.textContent;
+          clearTimeout(resetTimer);
+          if (!btn.dataset.originalText) btn.dataset.originalText = btn.textContent;
           btn.textContent = "copied";
-          setTimeout(function () { btn.textContent = was; }, 1400);
-        });
+          resetTimer = setTimeout(function () { btn.textContent = btn.dataset.originalText; }, 1400);
+        }).catch(function () {});
       });
     });
   }
📝 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.

  function initCopy() {
    $all("[data-copy]").forEach(function (btn) {
      var resetTimer;
      btn.addEventListener("click", function () {
        var src = $(btn.getAttribute("data-copy"));
        if (!src || !navigator.clipboard) return;
        var text = $all(".cmd", src)
          .map(function (line) {
            var dim = $(".dim", line);
            var t = line.textContent.replace(/^\$\s*/, "");
            if (dim) t = t.replace(dim.textContent, "");
            return t.trim();
          })
          .join("\n");
        navigator.clipboard.writeText(text || src.textContent.trim()).then(function () {
          clearTimeout(resetTimer);
          if (!btn.dataset.originalText) btn.dataset.originalText = btn.textContent;
          btn.textContent = "copied";
          resetTimer = setTimeout(function () { btn.textContent = btn.dataset.originalText; }, 1400);
        }).catch(function () {});
      });
    });
  }
🧰 Tools
🪛 ast-grep (0.44.1)

[error] 425-425: React's useState should not be directly called
Context: setTimeout(function () { btn.textContent = was; }, 1400)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)

🤖 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 `@web/app.js` around lines 410 - 430, The initCopy click handler can leave the
button stuck on "copied" because repeated clicks overwrite the saved label and
create overlapping untracked timers. Update the initCopy logic so each button
tracks its own original label and timeout state across clicks, clears or
replaces any existing reset timer before starting a new one, and restores the
label reliably after navigator.clipboard.writeText completes. Also add a
catch/finally path in the click handler to handle clipboard write failures and
ensure the button state is reset even when the promise rejects.

the merge landed models.py changes without regenerated json schemas
(claim.schema.json drifted) and carried an example screenshot rendered
in an environment whose search fell back to the substring backend.
both artifacts are regenerated from a clean python 3.12 venv with a
fresh editable install — the same shape the ci matrix builds — so the
committed bytes match what ci derives.
@github-actions github-actions Bot added the schemas json schemas and generated schema assets label Jul 6, 2026
the pull_request merge ref resolved decision-log-search.svg to a
corrupted copy committed to the base branch by mistake (rendered from
a working tree mid-branch-switch); with that commit reverted on test,
the merge tree's svg and its fresh render agree again. empty commit to
re-evaluate the merge ref.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adapters agent host adapters and install manifests ci github actions and automation cli command line interface docs documentation, specs, examples, and repo guidance mcp mcp, jsonl, and http surfaces openclaw openclaw integration packaging packaging, build metadata, and make targets review-ui browser review ui schemas json schemas and generated schema assets size: XL 1000 or more changed non-doc lines storage kb storage, migrations, schemas, and proposals tests tests and fixtures website static website

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants