Fix/openclaw plugin manifest#363
Conversation
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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis 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. ChangesCore Feature: Session Claims, Merge, Summarize, OpenClaw, Web UI
Example Decision-Log KB Fixture
Design Specs, Presentation, and Tutorials
Marketing Website Redesign
CI Workflows and Repo Infra
Estimated code review effort: 5 (Critical) | ~150 minutes Possibly related issues
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…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
There was a problem hiding this comment.
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; newsummarizeflows; 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). |
| 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) | ||
| ), | ||
| ) |
| 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 |
| 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) |
| 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" | ||
| ) |
| @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, | ||
| ) |
| """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. | ||
| """ |
| <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> |
| "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`.", |
There was a problem hiding this comment.
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 winAdd JSONL coverage for
kb.merge_pending
src/vouch/jsonl_server.pyregisterskb.merge_pending, buttests/test_jsonl_server.pydoesn’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 winFont-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 usefont-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 winSubprocess timeout may leave orphaned children when
shell=True.On
TimeoutExpired,subprocess.runkills the shell process but not necessarily grandchildren spawned byconfig.llm_cmd(e.g. a long-runningclaude -pinvocation that forks). For a summarizer command that hangs, this can leak processes across repeatedvouch summarize/SessionEndhook 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 NoneRegarding the
shell=True/unsanitized-command static-analysis flags on this block: the docstring already documentsllm_cmdas 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 winkeep the llm config namespace singular.
please confirm whether
distill.*is meant to be a future-only namespace. the shipped code already usescapture.summary_*insrc/vouch/storage.pyandsrc/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 valueConsider exposing PR number/URL as composite-action outputs.
The
create-pull-requeststep already producespull-request-number/pull-request-url, but the composite action doesn't re-expose them via anoutputs: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 winLabel is never removed once satisfying conditions are met.
Since
on.typesincludesedited/synchronize, the workflow re-runs when the PR body/branch changes, but the script only callsaddLabels— it never removesneeds-maintainerif 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
currentColorcasing 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 valueKeyword 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 winRepeated hardcoded vermillion-alpha literals instead of a token.
rgba(255,90,61, X)(matching--vermillion) is duplicated across many rules (e.g..rx-symbolbackground/border here,.feature:hover/.show-card:hover/.tile:hoverborders,codebackground,aborder-bottom, hero gradient). Since this file already centralizes color in custom properties, consider adding a couple of alpha-variant tokens (orcolor-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/.45with 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 valueReplace deprecated
word-break: break-word.Stylelint flags this as a deprecated keyword;
overflow-wrap: anywhereis 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 | 🔵 TrivialDocstring 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 everystatus()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
⛔ Files ignored due to path filters (7)
docs/img/examples/decision-log-diff.svgis excluded by!**/*.svgdocs/img/examples/decision-log-search.svgis excluded by!**/*.svgdocs/img/vouch-auto-capture-1270x760.pngis excluded by!**/*.pngdocs/img/vouch-plaintext-cited-auditable-1270x760.pngis excluded by!**/*.pngdocs/img/vouch-review-gate-1270x760.pngis excluded by!**/*.pngdocs/img/vouch-ui-review-deployed.pngis excluded by!**/*.pngdocs/presentation/vouch-claude-code-live.mp4is 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.yamlCHANGELOG.mdCLAUDE.mdREADME.mdadapters/claude-code/.claude/commands/vouch-recall.mdadapters/claude-code/.claude/commands/vouch-resolve-issue.mdadapters/claude-code/.claude/commands/vouch-start.mdadapters/claude-code/.claude/commands/vouch-status.mdadapters/claude-code/.claude/settings.jsonadapters/claude-code/install.yamladapters/openclaw/AGENTS.md.snippetadapters/openclaw/README.mdadapters/openclaw/install.yamladapters/openclaw/skills/vouch-propose-from-pr/SKILL.mdadapters/openclaw/skills/vouch-recall/SKILL.mdadapters/openclaw/skills/vouch-resolve-issue/SKILL.mdadapters/openclaw/skills/vouch-start/SKILL.mdadapters/openclaw/skills/vouch-status/SKILL.mdadapters/openclaw/vouch-context-engine.mjsdocs/getting-started.mddocs/presentation/vouch-claude-code-live.htmldocs/superpowers/specs/2026-07-04-company-brain-design.mddocs/superpowers/specs/2026-07-04-decision-distill-design.mddocs/superpowers/specs/2026-07-05-vouch-cloud-design.mddocs/tutorials/README.mddocs/tutorials/remember-across-sessions.mdexamples/decision-log/vouch/.gitignoreexamples/decision-log/vouch/audit.log.jsonlexamples/decision-log/vouch/claims/billing-data-requires-acid-guarantees-postgresql-is-the-prim.yamlexamples/decision-log/vouch/claims/critical-system-incidents-must-have-a-15-minute-response-sla.yamlexamples/decision-log/vouch/claims/free-tier-100-req-superseded.yamlexamples/decision-log/vouch/claims/free-tier-500-req.yamlexamples/decision-log/vouch/claims/free-tier-gets-100-requests-per-day-as-baseline.yamlexamples/decision-log/vouch/claims/incident-response-15min-sla.yamlexamples/decision-log/vouch/claims/use-postgres-for-billing.yamlexamples/decision-log/vouch/claims/use-postgresql-15-for-enhanced-replication-and-monitoring-fe.yamlexamples/decision-log/vouch/claims/vouch-starter-reviewed-knowledge.yamlexamples/decision-log/vouch/config.yamlexamples/decision-log/vouch/decided/20260703-091452-063145f6.yamlexamples/decision-log/vouch/decided/20260703-091452-4ffb9687.yamlexamples/decision-log/vouch/decided/20260703-091452-976471f3.yamlexamples/decision-log/vouch/decided/20260703-091453-4ca258b5.yamlexamples/decision-log/vouch/pages/edit-in-obsidian.mdexamples/decision-log/vouch/schema_versionexamples/decision-log/vouch/sources/1f8ef21996a0f59eeb5174071f5293fc3b864defe928b9ce7879022a8230dbc8/contentexamples/decision-log/vouch/sources/1f8ef21996a0f59eeb5174071f5293fc3b864defe928b9ce7879022a8230dbc8/meta.yamlexamples/decision-log/vouch/sources/3131367abbcefab105fa2fcaa36617bfdda15511cc8880145af113731e95ad6d/contentexamples/decision-log/vouch/sources/3131367abbcefab105fa2fcaa36617bfdda15511cc8880145af113731e95ad6d/meta.yamlexamples/decision-log/vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/contentexamples/decision-log/vouch/sources/be7aec64b0fc803a33cb3d610f67ae95e636877db20231ef72440a7cbe6b69d2/meta.yamlexamples/jsonl-quickstart/run.shopenclaw.plugin.jsonpackage.jsonpyproject.tomlsrc/vouch/capabilities.pysrc/vouch/capture.pysrc/vouch/cli.pysrc/vouch/health.pysrc/vouch/jsonl_server.pysrc/vouch/models.pysrc/vouch/openclaw/context_engine.pysrc/vouch/openclaw/rpc.pysrc/vouch/proposals.pysrc/vouch/server.pysrc/vouch/sessions.pysrc/vouch/storage.pysrc/vouch/summarize.pysrc/vouch/web/server.pysrc/vouch/web/static/app.csssrc/vouch/web/static/app.jssrc/vouch/web/templates/base.htmlsrc/vouch/web/templates/claim.htmlsrc/vouch/web/templates/queue.htmlsrc/vouch/web/templates/session.htmlsrc/vouch/web/templates/source.htmlsrc/vouch/web/templates/start_from.htmltests/test_capture.pytests/test_install_adapter.pytests/test_merge_pending.pytests/test_openclaw_context_engine.pytests/test_openclaw_plugin_load_real.pytests/test_openclaw_plugin_manifest.pytests/test_sessions.pytests/test_summarize.pytests/test_web.pyweb/app.cssweb/app.jsweb/base.cssweb/gittensor.htmlweb/how-it-works.htmlweb/index.htmlweb/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
There was a problem hiding this comment.
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 --autoexits 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)' .githubRepository: 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.ymlRepository: vouchdev/vouch
Length of output: 3594
Align the AI triage checks with the actual PR template.
.github/PULL_REQUEST_TEMPLATE.mdonly hasWhat changed,Why,What might break,VEP, andTests; there is noAI contributor,Track,Verification,Scope Expansion,Validation Failures, orCI Failuressection. As written, template-based AI PRs won’t matchhasTemplateSignature, andneedsMaintainerwill stay false unless the branch isai/orbot/. 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 testsRepository: 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 -nRepository: 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 -nRepository: 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') PYRepository: vouchdev/vouch
Length of output: 17817
🏁 Script executed:
#!/bin/bash set -euo pipefail sed -n '130,210p' src/vouch/sessions.pyRepository: 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_claimandkb_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 testsRepository: 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.pyRepository: vouchdev/vouch
Length of output: 50370
Add a proposal-aware MCP fallback
adapters/openclaw/skills/vouch-start/SKILL.md:15-19The CLI path accepts proposal ids, but the fallback only tries
kb_read_claimandkb_read_page. There’s no proposal read path here, so/vouch-startwon’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.
textkeeps 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 doneRepository: 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 doneRepository: 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 -nRepository: 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 -nRepository: 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.rejectfrom the advertised tool list doesn’t stop invocation:src/vouch/jsonl_server.pydispatches any method present inHANDLERS, andsrc/vouch/server.pystill 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 usevouch__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.pyRepository: 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 doneRepository: 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 doneRepository: 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 doneRepository: vouchdev/vouch
Length of output: 6359
Add JSONL envelope tests for the new methods.
tests/test_jsonl_server.pycovers other handlers, but notkb.list_sessions,kb.summarize_session, orkb.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) PYRepository: vouchdev/vouch
Length of output: 26527
Escape
session_idbefore globbing.safestill allows*,?, and[...], soprojects.glob(f"*/{safe}.jsonl")can widen to unrelated transcripts and quietly pick the first sorted match.glob.escapefixes 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".
wasis read frombtn.textContentat click time, and each click starts a new, untrackedsetTimeout. If a second click fires before the first timeout resolves,wascaptures"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.
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.
What changed
Why
What might break
VEP
Tests
make checkpasses locally (lint + mypy + pytest)CHANGELOG.mdupdated under## [Unreleased]Summary by CodeRabbit
New Features
Bug Fixes
Documentation