Skip to content

feat: add AI-powered Insights with multi-agent generation - #16

Merged
wesm merged 32 commits into
mainfrom
session-summary
Feb 24, 2026
Merged

feat: add AI-powered Insights with multi-agent generation#16
wesm merged 32 commits into
mainfrom
session-summary

Conversation

@wesm

@wesm wesm commented Feb 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Insights feature for AI-generated analysis of agent sessions, supporting Claude, Codex, and Gemini as generation backends
  • Full-stack: insights SQLite table with FTS5, Go HTTP handlers (GET/DELETE /api/v1/insights/{id}, POST /api/v1/insights/generate with SSE streaming), prompt builder with session context, and multi-agent CLI dispatch
  • Date range analysis: insights can cover a single day or a date range (e.g., a week). DB schema uses date_from/date_to columns. The generate endpoint validates both fields and supports date_to >= date_from
  • Mode-driven UI: type selector with three modes -- Daily Activity (single date), Date Range Activity (from/to inputs with 7-day and 30-day presets), and Agent Analysis (single date). Mode is derived reactively from store state
  • Svelte 5 Insights page with sidebar controls (type, date, project, agent), concurrent generation tasks with live status spinners, date display on in-progress tasks, and markdown content viewer with delete support
  • Structured API errors: ApiError class with status field for programmatic error handling; empty response bodies fall back to "API <status>" message
  • ListInsights capped at 500 rows with created_at DESC index to prevent unbounded queries
  • internal/insight package: prompt construction from session data with date-aware text ("Date" vs "Date Range"), streaming response parsing for Claude/Codex/Gemini CLI output
  • Session breadcrumb bar showing project name, agent badge (color-coded), and session start time
  • Header navigation with always-visible Sessions/Insights buttons and active state highlighting
  • Design polish: Inter font, refined color tokens for light/dark themes, tighter typography and spacing

Test plan

  • go test -tags fts5 ./... -- all Go tests pass (insights CRUD, filters, 500-row cap, date range round-trip, prompt builder with single/range dates, server handler validation)
  • npx vitest run -- all 360 frontend tests pass (insights store, date sync, mode switching, generate payloads, ApiError handling with empty-body fallback)
  • npx tsc --noEmit -- no type errors
  • go vet ./... -- clean
  • Manual: generate a Daily Activity insight, verify single date in list and content header
  • Manual: switch to Date Range Activity, verify from/to inputs and presets appear, generate and verify range displays
  • Manual: verify all insights appear in the list regardless of selected generation dates
  • Manual: verify in-progress task items show the date being analyzed
  • Manual: delete an insight, verify removal from list and content area

Generated with Claude Code

wesm and others added 22 commits February 23, 2026 16:11
Add a summaries system that generates markdown summaries of daily
agent activity using the Claude CLI. Summaries are append-only and
stored in SQLite, supporting both daily_activity and agent_analysis
types with optional per-project scoping.

Backend:
- Schema: summaries table with type/date/project/agent/model/prompt/content
- DB layer: InsertSummary, ListSummaries, GetSummary, DeleteSummary
- Summary package: prompt builder (queries sessions, assembles context)
  and generator (invokes `claude -p` via exec)
- Server: GET/POST endpoints for list, get, and SSE-streamed generation

Frontend:
- New #/summaries route with nav button in header
- SummariesPage: toolbar (date/type/project/prompt), summary list,
  markdown viewer
- Store with load/generate/cancel state management
- SSE client for streaming generation progress

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add multi-agent invocation for summary generation alongside Claude.
Each agent uses its native CLI with JSONL stream parsing:
- Codex: `codex exec --json --full-auto` with agent_message items
- Gemini: `gemini --output-format stream-json` with result events
- Claude: `claude -p --output-format stream-json` with result events

Includes agent selector in the UI, server-side validation with
default to "claude", and stream parser unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix stale filter race in generate(): capture filter snapshot at
  start and check on completion before prepending result
- Add 10-minute context.WithTimeout for summary generation
- Validate non-empty content before saving summary to DB
- Flush TextDecoder on EOF in both SSE readers in client.ts
- Add error event handling in parseStreamJSON for stream errors
- Add tests for stream error events and malformed JSON resilience

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change needsRebuild to return (bool, error) so probe failures
  propagate instead of silently proceeding with stale schema
- Add TestFindPruneCandidatesExcludesParents to verify parent
  sessions are excluded from prune candidates
- Remove "no such file" from TestMigrationRace allowed errors
  since the DB is pre-created in test setup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Only treat os.IsNotExist as "no DB exists" in needsRebuild;
  propagate permission/IO errors instead of masking them
- Add TestOpenProbeErrorPropagates for unreadable DB path
- Add summaries store test for filter-mismatch during generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split TestOpenProbeErrorPropagates into two subtests:
- StatPermissionError: removes execute on parent dir to trigger
  os.Stat EACCES (tests the non-ENOENT stat branch)
- ProbeReadError: removes read on file for SQLite probe failure

Both skip when running as root via os.Geteuid check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Skip TestOpenProbeErrorPropagates on Windows where chmod
  semantics don't enforce access denial
- Assert errors.Is(err, fs.ErrPermission) in StatPermissionError
  subtest for precise error class validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use --sandbox read-only for codex exec instead of --full-auto
- Drain stdout pipe before cmd.Wait to prevent hangs on parse errors
- Sanitize SSE error messages to avoid leaking internal paths/stderr
- Replace _global magic string with GlobalOnly bool in SummaryFilter
- Fix truncateString to use rune slice for safe multibyte truncation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SummariesPage was nested inside ThreeColumnLayout, causing a
double-sidebar (session list + summary list). Move it outside
the layout so it renders full-width with its own internal
two-column layout. Use explicit viewport height calculation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single-generation model with concurrent task support.
Each generate() call creates an independent task with its own
spinner, phase tracking, cancel button, and error state in the
sidebar. Completed insights auto-prepend to the list and
auto-select for viewing.

- Rename summaries store/page/route to insights throughout
- Add InsightTask interface with clientId-keyed Map of handles
- Support cancelTask(id), dismissTask(id), cancelAll()
- Show generating tasks with pulse animation and spinner dots
- Show errored tasks with dismiss button
- Content header with type badge, date, project, agent, time

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign the Insights page with refined visual treatment:

Sidebar:
- Compact 2x2 filter grid replacing single toolbar row
- Collapsible prompt textarea via toggle button
- Sticky "Active" section header with blinking live dot
- Shimmer bar animation on generating tasks (replaces pulse)
- Dismiss buttons hidden until hover for cleaner rows
- Error tasks get tinted background via color-mix

Content:
- Reading area with bg-primary background for contrast
- Pill-shaped type badges in content header
- Formatted date (e.g. "Mon, Jan 15") instead of raw ISO
- Model name in mono font, time right-aligned
- Max-width 720px on markdown body for readability
- Styled blockquotes with blue left border and tinted bg
- Table, hr, strong, and link styles for markdown
- Orbit animation for generating empty state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename the database table, Go packages, server handlers, API routes,
frontend types, and store references from summaries/Summary to
insights/Insight. The old summaries table is orphaned.

- internal/summary → internal/insight
- internal/db: Summary → Insight, SummaryFilter → InsightFilter
- internal/server: /api/v1/summaries → /api/v1/insights
- frontend: Summary → Insight, InsightType, InsightsResponse
- Store: summaries → items, selectedSummary → selectedItem

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the agent label out of the second-line metadata and into a
right-aligned mono-font label on each insight row.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stop filtering the sidebar list by insight type so daily activity and
agent analysis insights are visible together. The type selector now
only affects which type gets generated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the conditional Dashboard button with an always-visible
Sessions nav button. Both Sessions and Insights buttons highlight
based on the current route, so navigation works from either page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use generatingCount for Active header/Stop all instead of tasks.length
- Make task dismiss/cancel buttons accessible via :focus-visible and
  visible on touch devices
- Add overflow handling to .row-agent
- Switch viewport height from 100vh to 100dvh
- Sessions nav button deselects active session before navigating
- Sanitize BuildPrompt error in SSE response

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename Active section to Tasks with count matching tasks.length so
the section header is consistent with its contents. Keep live-dot and
Stop all gated on generatingCount. Add 100vh fallback before 100dvh
for browsers without dvh support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show a compact "Sessions / {project}" breadcrumb at the top of
the content area when viewing a session. Clicking "Sessions"
deselects and returns to the dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch primary font to Inter for crisper UI typography. Adjust
color tokens for refined borders and shadows in both themes.
Polish header, sidebar, breadcrumb, status bar, and insights
page with tighter spacing, better letter-spacing, and subtle
depth cues (active nav tint, generate button shadow).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add DELETE /api/v1/insights/{id} endpoint backed by the
existing DB.DeleteInsight method. Add deleteInsight API
client function, store deleteItem method, and a trash
button in the insight content header. Includes server
and store tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Display the session agent as a colored badge (blue for claude,
green for codex) and the session start date/time on the right
side of the breadcrumb bar when viewing a session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Assign return values to blank identifiers to satisfy golangci-lint
errcheck for the stdout drain calls in codex/gemini generators.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Feb 23, 2026

Copy link
Copy Markdown

roborev: Combined Review (6af7802)

Summary Verdict: 5 findings identified (2 High, 3 Medium) encompassing broken access control, build-breaking syntax, information leakage, DoS vulnerabilities
, and a frontend race condition.

High

1. Unauthenticated API Endpoints (Broken Access Control)

  • Files: internal/server/server.go:109, internal/server/insights.go:131, internal/insight/generate.go:1 03
  • Description: New endpoints POST /api/v1/insights/generate (which executes local AI CLIs) and DELETE /api/v1/insights/{id} are exposed without authorization checks. If the service is reachable beyond trusted localhost, attackers can trigger local command execution or perform destructive
    actions.
  • Remediation: Add authentication middleware and per-resource authorization checks before generation and deletion. Restrict binding to localhost if intended to be local-only, and enforce origin checks for browser-originated calls.

2. Invalid Svelte Syntax & Potential Stored XSS

  • Files:
    frontend/src/lib/components/insights/InsightsPage.svelte:219, 343, 349
  • Description: A corrupted tag (likely from a stray search-and-replace) appears as { @frontend/index.html renderMarkdown(insights.selectedItem .content)} instead of a valid Svelte HTML injection directive. This invalid syntax will completely break the frontend build. Furthermore, because this renders persisted AI-generated content, it presents a Stored XSS risk if the output is not rigorously sanitized.
  • Remediation: Fix the syntax error by reverting to S
    velte's proper {@html ...} directive. Ensure renderMarkdown employs a robust HTML sanitizer (e.g., DOMPurify) and apply a Content Security Policy (CSP) to block inline script execution.

Medium

3. Information Leakage via Error Paths

  • Files: internal/ insight/generate.go:72, 85, internal/server/insights.go:52, 199
  • Description: Error handling paths directly return or log raw CLI stderr/stdout and database error strings. This behavior can leak sensitive prompt/session content, internal paths, or
    operational details to the client.
  • Remediation: Return generic client errors in API responses. Keep detailed errors strictly server-side, and redact sensitive prompt/content/stderr before logging.

4. Resource Exhaustion (DoS) via Unbounded Request Parsing

  • Files: internal/server/insights .go:136, 144, internal/insight/prompt.go:93
  • Description: The handleGenerateInsight endpoint decodes the incoming JSON request body without imposing size limits and passes unbounded input to external CLIs (which can run for up to 1
    0 minutes). Malicious actors can send excessively large payloads to cause an Out-Of-Memory (OOM) crash or easily exhaust system resources.
  • Remediation: Wrap http.Request.Body with http.MaxBytesReader (e.g., 1MB limit), enforce strict maximum
    lengths on all input fields (prompt, project, date, type), and implement per-client rate and concurrency limits for generation.

5. Race Condition in Insights Store

  • Files: frontend/src/lib/stores/insights.svelte.ts:54, 1 49

  • Description: The load() function can race with generate() completion. If a load() request is in-flight while generate() prepends a new insight locally, the delayed load() response can overwrite the items array, erasing the newly generated and selected insight.

  • Remediation: Invalidate in-flight loads before applying local generation results (e.g., increment a version counter), or consistently refresh the list via load() after generation and deduplicate items by id.


Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

@roborev-ci

roborev-ci Bot commented Feb 23, 2026

Copy link
Copy Markdown

roborev: Combined Review (f0fca15)

Summary Verdict: The PR implements the end-to-end Insights feature but introduces high-severity security issues (missing authentication, XSS vulnerabilities) and requires fixes for DoS risks, CORS, and background
polling leaks before merging.

High Severity

  • Unauthenticated Insights Endpoints
    • Files: internal/server/server.go:106, internal/server/insights.go:21, internal/server/insights.go:133

Issue: New insights endpoints are exposed without authentication/authorization checks. This includes read (GET), destructive (DELETE), and command-triggering generation (POST /insights/generate) operations. If the service is reachable by untrusted clients, this enables unauthorized data access, deletion, and abuse of local AI CLIs.

  • Remediation: Add auth middleware on /api/v1/insights* routes, enforce per-user authorization, and explicitly reject non-loopback access for local-only mode.

  • Stored XSS and Syntax Error in Markdown Rendering

    • Files: frontend/src/ lib/components/insights/InsightsPage.svelte (lines 214, 301)
    • Issue: The application renders AI-generated markdown using a raw HTML directive. There is a syntax artifact ({@frontend/index.html ...} instead of Svelte's {@ html ...}) that will cause a build failure. More importantly, rendering untrusted AI-generated content directly into the DOM creates a Stored Cross-Site Scripting (XSS) vulnerability via indirect prompt injection, as models could be manipulated into outputting malicious payloads.
    • Remediation: Fix the directive syntax
      to Svelte's {@html ...} and strictly sanitize the output of renderMarkdown using a trusted library like DOMPurify before rendering the HTML string.

Medium Severity

  • Resource Exhaustion DoS Vector

    • Files: internal/server/insights.go: 133, internal/server/insights.go:190
    • Issue: handleGenerateInsight accepts an unbounded JSON body/prompt and can spawn long-running subprocesses (up to 10 minutes) per request, with no visible concurrency or rate limits.
  • Remediation: Apply http.MaxBytesReader, enforce maximum lengths for input fields (prompt, project, type, date), and add server-side rate limiting/concurrency caps for generation jobs.

  • CORS Allowlist Missing DELETE Method

    • Files: internal/server /server.go:109, internal/server/server.go:226
    • Issue: The DELETE /api/v1/insights/{id} endpoint is registered, but Access-Control-Allow-Methods still only advertises GET, POST, OPTIONS. Cross-origin browser clients will fail the CORS preflight for delete requests.
    • Remediation: Include DELETE in the CORS allow methods list to align with registered API verbs.
  • Background Session Polling Leak

    • Files: frontend/src/App.svelte: 30, frontend/src/lib/components/layout/AppHeader.svelte:88
    • Issue: Navigating to Insights changes the route but does not clear or disable active session handling. The App.svelte effect continues to load and watch the session in the background, consuming
      unnecessary resources while the session UI is hidden.
    • Remediation: Gate the session watch/load logic on router.route === "sessions" or explicitly unwatch the session when navigating to the Insights page.

Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

wesm and others added 3 commits February 23, 2026 19:05
- Fix .breadcrumb-current ellipsis with flex: 1; min-width: 0
- Wrap badge+time in .breadcrumb-meta right-aligned container
- Add default background on .agent-badge for unknown agents
- Load Inter as variable font (wght@400..700) for 450/650 weights
- Treat 404 in deleteItem as successful local removal
- Add store tests: setAgent, cancelAll, load error/stale handling,
  deleteItem 404 reconciliation
- Add API client tests: deleteInsight, listInsights/getInsight query
  serialization, generateInsight SSE parsing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Include DELETE in Access-Control-Allow-Methods header so
  cross-origin preflight succeeds for insight deletion
- Add test verifying all HTTP methods in CORS header
- Add .roborev.toml with threat model documenting that this is
  a localhost-only tool (no auth, rate limiting, or input size
  limits needed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ApiError class with status field to client.ts. deleteItem now
checks e.status === 404 instead of e.message.includes("404").
Updated tests to assert on ApiError type and status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Feb 24, 2026

Copy link
Copy Markdown

roborev: Combined Review (33facca)

Verdict: The PR introduces a few medium-severity issues regarding potential sensitive data leakage in logs and background session syncing regressions that should be addressed.

Medium

1. Potential sensitive data leak
and log-forging via raw stderr/stdout logging

  • Description: CLI failures are wrapped with raw stderr/stdout content, and then logged (log.Printf("insight generate error: %v", err)). This can leak sensitive prompt/session data into logs and allows
    log-forging via untrusted newline/control characters in model/CLI output.
  • Suggested Remediation: Return/log structured, sanitized errors only. Avoid embedding raw stdout/stderr in error strings; if needed for debugging, redact and truncate, and escape control characters before logging.

Files:

  • internal/insight/generate.go:70
  • internal/insight/generate.go:84
  • internal/insight/generate.go:129
  • internal/insight/generate.go:258
  • internal/server/insights.go:194

2. Background session sync continues while on Insights route (regression/perf)

  • Description: The new Insights nav button only changes route (router.navigate("insights")) and does not clear/disable active-session
    watchers. Existing session-loading/watch effects are keyed to activeSessionId, so if a session was selected before switching routes, message polling/reloads can continue unnecessarily in the background.
  • Suggested Remediation: Either deselect/unwatch when entering Insights, or gate message/session watch effects by
    router.route === "sessions".
  • Files:
    • frontend/src/lib/components/layout/AppHeader.svelte
    • frontend/src/App.svelte

Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

@roborev-ci

roborev-ci Bot commented Feb 24, 2026

Copy link
Copy Markdown

roborev: Combined Review (a017507)

Verdict: The PR introduces the Insights feature end-to-end but requires fixes for a Svelte compilation error, a session state regression, and potential sensitive data leaks in error logs.

High

  1. Invalid Svelte syntax
    File: frontend/src/lib
    /components/insights/InsightsPage.svelte:285

    Issue: Invalid Svelte syntax { @frontend /index.html renderMarkdown(...) }. This appears to be a broken find-and-replace for { @html } and will cause a Svelte compilation error.
    Remediation: Fix the syntax to correctly render HTML, likely by reverting to { @html renderMarkdown(...) }.

Medium

  1. Session state is reset when navigating to Insights (regression)
    File: frontend/src/App.svelte:125

Issue: router.route changes trigger sessions.initFromParams(params) and sessions.load() unconditionally. Moving to #/insights (usually no session params) clears active session and resets filters (e.g., project/date), and also does unnecessary session reloads while on Insights.

Remediation: Only run sessions.initFromParams/load when router.route === "sessions" (or preserve existing session filters when route is insights).

  1. Sensitive data leak in error logs
    File: [internal/insight/generate.go:69](/
    home/roborev/.roborev/clones/wesm/agentsview/internal/insight/generate.go:69), [internal/insight/generate.go:83](/home/roborev/.roborev/clones/wesm/agentsview/internal/insight
    /generate.go:83), internal/insight/generate.go:129, internal/insight/generate.go:252
    , [internal/server/insights.go:194](/home/roborev/.roborev/clones/wesm/agentsview/
    internal/server/insights.go:194)
    Issue: Error paths include raw CLI stderr and sometimes raw stdout in returned errors, and the server logs those errors directly. This can leak sensitive prompt/session content, model output, or local environment details into logs.

Remediation: Return/log sanitized error codes/messages by default; redact or truncate CLI output; gate full raw output behind explicit debug mode only.


Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

Replace single `date` column with `date_from` and `date_to` in the
insights table to support analyzing date ranges (e.g., a week) in
addition to single days. The list endpoint no longer filters by date,
returning all insights reverse-chronologically. The UI shows two date
inputs with quick presets (Today, 7 days, 30 days), and each insight
row displays its analyzed date range.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wesm and others added 6 commits February 23, 2026 19:44
The type selector is now on its own row with three modes:
- Daily Activity: single date input (default)
- Date Range Activity: from/to inputs with 7-day and 30-day presets
- Agent Analysis: single date input

The presets and range inputs only appear when Date Range Activity is
selected. Single-date modes keep dateFrom and dateTo in sync. List
rows and content header labels reflect the actual date range of each
insight (Daily vs Range vs Analysis).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ApiError message falls back to "API <status>" when body is empty,
  preventing blank error text in UI callers
- Add fetchJSON error path test covering ApiError instanceof and
  status, plus empty-body fallback
- Cap ListInsights at 500 rows and add created_at DESC index to
  prevent unbounded queries as data grows
- No-sessions prompt text now says "date range" when DateFrom != DateTo
- Add multi-day range tests: prompt "Date Range" header, DB round-trip
  with distinct date_from/date_to values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
uiMode is now a $derived value that infers the correct mode from
insights.type and dateFrom/dateTo, with modeOverride for explicit
user selection. This prevents stale range submissions when the UI
shows single-date mode but the store still holds a range from prior
interaction.

Add store-level tests verifying date sync on mode switch and that
generate() sends correct date_from/date_to for both single-day and
range configurations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Display the date (or date range) being analyzed next to the type
label in in-progress and errored task items, so users can see which
date each generation targets without waiting for completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove modeOverride entirely — deriveMode() already infers the
correct UI mode from store state (type + dateFrom/dateTo), and
handleModeChange updates the store immediately. The override was
redundant and could prevent the UI from reflecting later store
changes.

Add test verifying ListInsights caps at 500 rows with newest-first
ordering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the user selects Date Range Activity while dateFrom === dateTo,
expand dateTo by 6 days so the derived uiMode switches to
range_activity and the range controls appear. Without this, the
dropdown selection had no effect and snapped back immediately.

Tighten ListInsights cap test to assert exact boundary IDs (newest
first, two oldest excluded) rather than a weak ordering check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Feb 24, 2026

Copy link
Copy Markdown

roborev: Combined Review (890cf0c)

The PR implements the new Insights feature but requires addressing critical functional regressions and medium-severity security issues.

High

  • Database loss on upgrade

    • File(s): internal
      /db/db.go
      , [internal/db/db.go](/home/roborev/.roborev/clones/wesm/agents
      view/internal/db/db.go#L137)
    • Description: needsRebuild() treats a missing insights.date_from as a rebuild trigger. For users upgrading from versions without the insights table, this path drops and recreates the DB, losing
      all existing session data.
    • Suggested fix: Do not use a missing insights table/column as a full-rebuild condition. Add it via an additive migration (CREATE TABLE IF NOT EXISTS insights) and reserve rebuilds for truly incompatible schema states.
  • Invalid Svelte syntax
    (Build Failure)

    • File(s): frontend/src/lib/components/insights/InsightsPage.svelte, .roborev.toml
    • Description: An errant global find-and-replace for the string html changed Svelte's
      raw HTML rendering tag from { @html renderMarkdown(...)} to { @frontend/index.html renderMarkdown(...)}. This is invalid syntax and will cause the frontend build to fail.
    • Suggested fix: Revert { @frontend/index.html back to { @html in both
      the Svelte component and the documentation in .roborev.toml.

Medium

  • Untrusted execution paths for AI CLI tools

    • File(s): [internal/insight/generate.go](/home/roborev/.roborev/clones/wes
      m/agentsview/internal/insight/generate.go) (Lines 40, 61, 103, 232)
    • Description: The server resolves claude/codex/gemini with exec.LookPath from the runtime
      PATH and executes the resolved binary. If PATH is tampered with or includes writable locations, a malicious binary could be executed.
    • Suggested fix: Use fixed absolute binary paths from a trusted configuration, validate ownership/permissions, and run with a minimized trusted PATH.

Sensitive data leakage in error logs
* File(s): internal/insight/generate.go (Lines 73, 86,
136, 264), internal/server/insights.go
* Description: CLI stderr/raw output
are embedded into returned errors and subsequently logged by the server. This can leak sensitive prompt/session content or tokens into the logs.
* Suggested fix: Do not include raw stderr/stdout in returned errors by default. Log only sanitized summaries (or gated debug logs), with truncation/red
action.

  • "Date Range Activity" mode UI bug
    • File(s): [frontend/src/lib/components/insights/InsightsPage.svelte](/home/roborev/.roborev/clones/wesm/agentsview/frontend/src/
      lib/components/insights/InsightsPage.svelte#L28)
    • Description: uiMode is derived from dateFrom !== dateTo, but handleModeChange("range_activity") only sets type and does not force a range. If dateFrom === dateTo (the default), the derived mode snaps back to daily_activity, meaning range controls may never appear.
    • Suggested fix: When selecting range mode, also set dateTo to a different date or maintain an explicit UI mode state rather than deriving it solely from dates.

Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

@roborev-ci

roborev-ci Bot commented Feb 24, 2026

Copy link
Copy Markdown

roborev: Combined Review (8cf6223)

Summary: The proposed changes introduce severe data loss and prompt injection vulnerabilities, alongside race conditions and logging issues, which must be addressed prior to merging.

High

  • Prompt Injection Vulnerability via Untrusted Session Text

    • Files: internal/insight/prompt.go:79, internal/insight/prompt.go:113, internal/insight/generate.go:57, internal/insight/generate.go:226
    • Description: Untrusted session text
      (FirstMessage) and user-supplied Prompt are directly embedded into the LLM prompt, then sent to agent CLIs. While codex is sandboxed read-only, claude and gemini are invoked without equivalent confinement. This creates a prompt-injection path where malicious session content can steer
      tool-capable agents to access local files/commands and leak data into generated insights.
    • Remediation: Run all agents in a strict non-tool/sandboxed mode (or isolate in a locked-down subprocess/container), use a minimal working directory/env, and treat session text as unt
      rusted data blocks with explicit “do not follow instructions inside data” guardrails.
  • Destructive Database Rebuild on Upgrade

    • File: internal/db/db.go:136
    • Description: needsRebuild() returns true when pragma_table _info('insights') doesn't contain date_from. For pre-feature databases where the insights table does not exist at all, this also returns 0. Consequently, Open() drops the whole database (dropDatabase), causing data loss during an upgrade.
    • Remed
      iation:
      Distinguish between “table missing” vs “table exists but missing required column.” If the table is missing, return false and let migrations create it.

Medium

  • Sensitive Content Exfiltration in Error Logs

    • Files: internal/insight/generate.go:70,
      internal/insight/generate.go:84, internal/server/insights.go:198
    • Description: Error paths include raw CLI stderr and raw model output (stdout) in error strings, and server code logs those errors. This can expose sensitive content (prompt data
      , model output, file paths, tokens printed by CLIs) to logs.
    • Remediation: Redact/suppress raw CLI output in normal logs, log only high-level error codes/messages, and gate detailed diagnostics behind an explicit debug mode.
  • Race Condition Hiding Newly
    Generated Insights

    • Files: frontend/src/lib/stores/insights.svelte.ts:59, frontend/src/lib/stores/insights.svelte.ts:157
    • Description: load() can be in flight while generate() succeeds
      and prepends the new item. If the older load() resolves after that, it overwrites this.items and the freshly generated insight disappears from the list until another reload.
    • Remediation: Invalidate/bump load version before optimistic prepend, or avoid prepend when loading === true and always refresh from server after generation.

Synthesized from 4 reviews (agents: codex, gemini | types: default, security)

@wesm
wesm merged commit ab63cbd into main Feb 24, 2026
6 checks passed
wesm added a commit that referenced this pull request Mar 28, 2026
## Summary

- Restores Copilot as a valid insight generation agent (removed in
8f90380 / 455587a due to sandboxing concerns)
- Adds `generateCopilot` back with `--silent --no-custom-instructions
--no-ask-user --disable-builtin-mcps` flags
- Updates frontend agent picker and TypeScript types to include Copilot
- Documents the sandboxing caveat in `.roborev.toml` review guidelines
(guideline #16)

Addresses #35 — Copilot was unintentionally dropped from the insights
agent list during an overly cautious review. The sandboxing difference
(`--disable-builtin-mcps` vs full no-tools modes) is real but
acceptable: agentsview operates on the user's own session data under the
same trust model as running agents on codebases.

## Test plan

- [x] `TestValidAgents` includes copilot
- [x] `TestGenerateCopilot_CLIFlags` verifies correct CLI invocation
- [x] `TestGenerateCopilot_EmptyResult` verifies error on empty output
- [x] `TestGenerateCopilot_PreservesBlankLines` verifies multi-paragraph
output
- [x] All existing insight tests still pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cursor Bot referenced this pull request in diazMelgarejo/periscope Jun 1, 2026
## Summary

- **Insights feature** for AI-generated analysis of agent sessions,
supporting Claude, Codex, and Gemini as generation backends
- Full-stack: `insights` SQLite table with FTS5, Go HTTP handlers
(`GET/DELETE /api/v1/insights/{id}`, `POST /api/v1/insights/generate`
with SSE streaming), prompt builder with session context, and
multi-agent CLI dispatch
- **Date range analysis**: insights can cover a single day or a date
range (e.g., a week). DB schema uses `date_from`/`date_to` columns. The
generate endpoint validates both fields and supports `date_to >=
date_from`
- **Mode-driven UI**: type selector with three modes -- Daily Activity
(single date), Date Range Activity (from/to inputs with 7-day and 30-day
presets), and Agent Analysis (single date). Mode is derived reactively
from store state
- Svelte 5 Insights page with sidebar controls (type, date, project,
agent), concurrent generation tasks with live status spinners, date
display on in-progress tasks, and markdown content viewer with delete
support
- **Structured API errors**: `ApiError` class with `status` field for
programmatic error handling; empty response bodies fall back to `"API
<status>"` message
- **ListInsights capped at 500 rows** with `created_at DESC` index to
prevent unbounded queries
- `internal/insight` package: prompt construction from session data with
date-aware text ("Date" vs "Date Range"), streaming response parsing for
Claude/Codex/Gemini CLI output
- **Session breadcrumb bar** showing project name, agent badge
(color-coded), and session start time
- **Header navigation** with always-visible Sessions/Insights buttons
and active state highlighting
- Design polish: Inter font, refined color tokens for light/dark themes,
tighter typography and spacing

## Test plan

- [x] `go test -tags fts5 ./...` -- all Go tests pass (insights CRUD,
filters, 500-row cap, date range round-trip, prompt builder with
single/range dates, server handler validation)
- [x] `npx vitest run` -- all 360 frontend tests pass (insights store,
date sync, mode switching, generate payloads, ApiError handling with
empty-body fallback)
- [x] `npx tsc --noEmit` -- no type errors
- [x] `go vet ./...` -- clean
- [ ] Manual: generate a Daily Activity insight, verify single date in
list and content header
- [ ] Manual: switch to Date Range Activity, verify from/to inputs and
presets appear, generate and verify range displays
- [ ] Manual: verify all insights appear in the list regardless of
selected generation dates
- [ ] Manual: verify in-progress task items show the date being analyzed
- [ ] Manual: delete an insight, verify removal from list and content
area

Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
cursor Bot referenced this pull request in diazMelgarejo/periscope Jun 1, 2026
## Summary

- Restores Copilot as a valid insight generation agent (removed in
8f90380 / 455587a due to sandboxing concerns)
- Adds `generateCopilot` back with `--silent --no-custom-instructions
--no-ask-user --disable-builtin-mcps` flags
- Updates frontend agent picker and TypeScript types to include Copilot
- Documents the sandboxing caveat in `.roborev.toml` review guidelines
(guideline #16)

Addresses #35 — Copilot was unintentionally dropped from the insights
agent list during an overly cautious review. The sandboxing difference
(`--disable-builtin-mcps` vs full no-tools modes) is real but
acceptable: agentsview operates on the user's own session data under the
same trust model as running agents on codebases.

## Test plan

- [x] `TestValidAgents` includes copilot
- [x] `TestGenerateCopilot_CLIFlags` verifies correct CLI invocation
- [x] `TestGenerateCopilot_EmptyResult` verifies error on empty output
- [x] `TestGenerateCopilot_PreservesBlankLines` verifies multi-paragraph
output
- [x] All existing insight tests still pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wesm
wesm deleted the session-summary branch June 25, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant