Skip to content

feat: add SonarCloud CI workflow#397

Merged
jerry609 merged 4 commits intodevfrom
feat/sonarcloud-ci
Mar 15, 2026
Merged

feat: add SonarCloud CI workflow#397
jerry609 merged 4 commits intodevfrom
feat/sonarcloud-ci

Conversation

@jerry609
Copy link
Owner

@jerry609 jerry609 commented Mar 15, 2026

Summary

  • add a dedicated SonarCloud GitHub Actions workflow for push/PR analysis on dev and master
  • generate Python coverage.xml and web lcov.info so SonarCloud can ingest backend and frontend coverage
  • reuse a shared backend offline test gate script from both the main CI job and the SonarCloud workflow

Validation

  • npm run test:coverage
  • YAML parse check for .github/workflows/sonarcloud.yml
  • TOML parse check for pyproject.toml

Notes

  • SonarCloud still requires repository-level SONAR_TOKEN secret plus SONAR_ORGANIZATION and SONAR_PROJECT_KEY variables after merge
  • backend coverage execution is configured for GitHub Actions Python 3.11; local shell here is Python 3.9.7

Summary by CodeRabbit

  • New Features

    • Added Agent Dashboard with multiple views: panels, kanban board, and task dependency graph
    • Enabled real-time agent activity streaming with live event updates
    • Added file change tracking and inline diff viewer for monitoring code modifications
    • Integrated Codex delegation workflow for offloading tasks
    • Added SonarCloud code quality scanning to CI/CD pipeline
  • Documentation

    • Comprehensive architecture and codebase documentation
    • Phase-based roadmap and requirements updates
  • Tests

    • Added code coverage reporting
    • Expanded test suite with offline execution gates

Copilot AI review requested due to automatic review settings March 15, 2026 05:11
@vercel
Copy link

vercel bot commented Mar 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
paper-bot Ready Ready Preview, Comment Mar 15, 2026 6:37am

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

Warning

Rate limit exceeded

@jerry609 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 51 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 88fb05ea-a854-4593-b769-e2350b41c7a7

📥 Commits

Reviewing files that changed from the base of the PR and between 3243877 and ab4dfd8.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • .github/workflows/sonarcloud.yml
  • .gitignore
  • pyproject.toml
  • requirements-ci.txt
  • scripts/ci/run_backend_offline_gates.sh
  • sonar-project.properties
  • web/package.json
  • web/vitest.config.ts
📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive agent event vocabulary and real-time streaming architecture, spanning backend Python and frontend React layers. It adds EventType constants, event-emission helpers, Codex delegation lifecycle tracking, a three-panel dashboard UI (with panels, Kanban, and DAG views), file-change tracking, and end-to-end SSE-based event consumption with Zustand state management.

Changes

Cohort / File(s) Summary
Planning & Documentation
.planning/PROJECT.md, .planning/REQUIREMENTS.md, .planning/ROADMAP.md, .planning/STATE.md, .planning/codebase/*, .planning/phases/*, .planning/research/*
Extensive updates reflecting shift from PostgreSQL migration to agent-agnostic dashboard architecture; v1.2 milestone definition; Phase 8–11 detailed plans for event vocabulary, three-panel dashboard, Codex bridge, and DAG visualization; comprehensive codebase structure and concerns documentation.
Event Vocabulary & Backend Helpers
src/paperbot/application/collaboration/message_schema.py, src/paperbot/application/collaboration/agent_events.py, src/paperbot/mcp/tools/_audit.py
New EventType class with 14 constants; make_lifecycle_event and make_tool_call_event factories; audit module migrated to use EventType constants instead of raw strings.
Codex Delegation & Agent Board
src/paperbot/api/routes/agent_board.py, src/paperbot/repro/orchestrator.py
Four CODEX_* EventType additions; _emit_codex_event helper and _get_event_log_from_container for event bus integration; event emission at dispatch, acceptance, completion, and failure points; _should_overflow_to_codex environment-driven overflow gate in orchestrator.
Frontend Event Types & Parsers
web/src/lib/agent-events/types.ts, web/src/lib/agent-events/parsers.ts
Comprehensive TypeScript vocabulary (AgentStatus, ToolCallEntry, FileTouchedEntry, CodexDelegationEntry, ScoreEdgeEntry, etc.); six parser functions (parseActivityItem, parseAgentStatus, parseToolCall, parseFileTouched, parseCodexDelegation, parseScoreEdge) with robust null-handling; deriveHumanSummary for readable event text.
Frontend State Management
web/src/lib/agent-events/store.ts, web/src/lib/agent-events/useAgentEvents.ts
Zustand store (useAgentEventStore) tracking feed, agent statuses, tool calls, file touches, codex delegations, score edges, and kanban tasks with capped collections and upsert semantics; SSE hook consuming /api/events/stream with retry logic and parser dispatch.
Frontend UI Components — Events & Activity
web/src/components/agent-events/ActivityFeed.tsx, web/src/components/agent-events/AgentStatusPanel.tsx, web/src/components/agent-events/ToolCallTimeline.tsx
Real-time activity feed rendering (color-coded by event type); agent status badges (idle, working, completed, errored); tool call timeline with duration, args, and error details.
Frontend UI Components — Dashboard Panels & Views
web/src/components/agent-dashboard/TasksPanel.tsx, web/src/components/agent-dashboard/FileListPanel.tsx, web/src/components/agent-dashboard/InlineDiffPanel.tsx, web/src/components/agent-dashboard/KanbanBoard.tsx, web/src/app/agent-dashboard/page.tsx
Three-panel layout (TasksPanel left rail, ActivityFeed center, FileListPanel right) with resizable SplitPanels; Kanban board rendering five columns (Planned, In Progress, Review, Done, Blocked) with agent badges and Codex error surfacing; inline diff viewer; view-mode toggle (panels/kanban) in dashboard page.
Frontend UI Components — DAG Visualization
web/src/lib/agent-events/dag.ts, web/src/components/agent-dashboard/AgentDagNodes.tsx, web/src/components/agent-dashboard/AgentDagEdges.tsx, web/src/components/agent-dashboard/AgentDagPanel.tsx, web/src/lib/store/studio-store.ts
Pure DAG builder functions (computeTaskDepths, buildDagNodes, buildDagEdges, taskStatusToDagStyle); ReactFlow node/edge components (TaskDagNode, ScoreFlowEdge); AgentDagPanel integrating ReactFlow with store-driven tasks; AgentTask gains optional depends_on field; third view mode (dag) added to dashboard page.
Sidebar & Layout
web/src/components/layout/Sidebar.tsx
Added "Agent Dashboard" route entry with Monitor icon to sidebar navigation.
Configuration & CI/CD
.github/workflows/ci.yml, .github/workflows/sonarcloud.yml, .gitignore, pyproject.toml, requirements-ci.txt, sonar-project.properties, scripts/ci/run_backend_offline_gates.sh, web/package.json, web/vitest.config.ts
Run offline gates via shell script; new SonarCloud workflow with separate scan and fork notice jobs; coverage configuration (pytest-cov, vitest coverage); SonarQube project config; web dependencies updated (testing libraries, coverage, jsdom); vitest config extended for environment and coverage reporting.
Claude Code Sub-Agent
.claude/agents/codex-worker.md
New agent definition documenting Codex delegation protocol via agent-board API with four-step curl-based workflow (confirm task, dispatch, stream execution, report result).
Comprehensive Test Coverage
tests/unit/test_agent_events_vocab.py, tests/unit/test_codex_overflow.py, tests/unit/test_agent_board_codex_events.py, web/src/lib/agent-events/*.test.ts, web/src/components/agent-dashboard/*.test.tsx
Unit tests validating EventType constants, lifecycle/tool-call helpers, codex overflow logic, event emission; TypeScript tests for parsers, store, DAG builders, and UI components (Kanban, DAG panel).

Sequence Diagram(s)

sequenceDiagram
    participant Backend as Backend<br/>(Python)
    participant EventLog as EventLog<br/>(Bus)
    participant SSE as SSE Stream<br/>/api/events/stream
    participant Parser as Parser<br/>(TypeScript)
    participant Store as Zustand Store
    participant UI as UI Components

    Backend->>Backend: Emit event via<br/>make_lifecycle_event or<br/>_emit_codex_event
    Backend->>EventLog: Append AgentEventEnvelope
    EventLog->>SSE: Fan-out event via stream
    SSE->>Parser: Receive raw envelope
    Parser->>Parser: Parse to typed entry<br/>(ActivityFeedItem, ToolCallEntry,<br/>CodexDelegationEntry, etc.)
    Parser->>Store: Dispatch store action<br/>(addFeedItem, updateAgentStatus,<br/>addToolCall, addCodexDelegation,<br/>addScoreEdge)
    Store->>Store: Update state<br/>(bounded collections,<br/>upsert by id)
    Store->>UI: Trigger re-render via<br/>Zustand selector
    UI->>UI: Display in ActivityFeed,<br/>KanbanBoard, AgentDagPanel,<br/>or ToolCallTimeline
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Events flow like carrots down the stream,
From Python's heart to TypeScript's dream,
Three panels dance, a Kanban glides,
While DAGs whisper of task-flow tides,
Real-time magic, bundled tight—
A dashboard born to set things right! 🎨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/sonarcloud-ci

@github-actions
Copy link

github-actions bot commented Mar 15, 2026

Vercel Preview

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds SonarCloud CI integration and bundles a significant amount of previously-developed feature code (phases 8–11) including an agent event vocabulary system, a three-panel agent dashboard, Codex delegation bridge, and DAG visualization. The CI changes extract a shared backend test script and configure coverage reporting for both Python and TypeScript.

Changes:

  • Add SonarCloud GitHub Actions workflow with Python coverage (XML) and frontend coverage (lcov), plus a shared backend offline test gate script reused by both CI and SonarCloud workflows
  • Introduce backend EventType constants, agent event helpers, Codex delegation event emission in agent_board.py, and an overflow routing stub in orchestrator.py
  • Add frontend agent dashboard with activity feed, tool call timeline, kanban board, DAG visualization, and supporting Zustand store/parsers/types

Reviewed changes

Copilot reviewed 84 out of 86 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
.github/workflows/sonarcloud.yml New SonarCloud CI workflow with backend + frontend coverage
.github/workflows/ci.yml Refactored to use shared offline gates script
scripts/ci/run_backend_offline_gates.sh Shared backend test runner script
sonar-project.properties SonarCloud project configuration
pyproject.toml Added pytest-cov and coverage config
requirements-ci.txt Added pytest-cov
web/vitest.config.ts Added coverage config, jsdom environment globs
web/package.json Added test:coverage script, testing-library, jsdom, coverage-v8
src/paperbot/application/collaboration/message_schema.py Added EventType constants class
src/paperbot/application/collaboration/agent_events.py New lifecycle/tool-call event helpers
src/paperbot/mcp/tools/_audit.py Migrated to EventType constants
src/paperbot/repro/orchestrator.py Added _should_overflow_to_codex stub
src/paperbot/api/routes/agent_board.py Added _emit_codex_event helper and call sites
tests/unit/test_*.py New backend tests for events, overflow, codex events
web/src/lib/agent-events/* Types, parsers, store, SSE hook, DAG utils
web/src/components/agent-events/* ActivityFeed, AgentStatusPanel, ToolCallTimeline
web/src/components/agent-dashboard/* KanbanBoard, TasksPanel, FileListPanel, InlineDiffPanel, DAG panels
web/src/app/agent-events/page.tsx Debug SSE page
web/src/app/agent-dashboard/page.tsx Agent dashboard page with panels/kanban toggle
web/src/components/layout/Sidebar.tsx Added Agent Dashboard nav entry
web/src/lib/store/studio-store.ts Added depends_on field to AgentTask
.claude/agents/codex-worker.md Claude Code sub-agent definition
.planning/** Phase planning/validation/summary docs
.gitignore Added coverage and SonarCloud artifacts
Files not reviewed (1)
  • web/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +828 to +833
await _emit_codex_event(
"codex_dispatched",
task,
session,
{"assignee": task.assignee},
)
Comment on lines +8 to +36
python -m pytest -q "$@" \
tests/unit/test_scholar_from_config.py \
tests/unit/test_source_registry_modes.py \
tests/unit/test_arq_worker_settings.py \
tests/unit/test_jobs_routes_import.py \
tests/unit/test_dailypaper.py \
tests/unit/test_paper_judge.py \
tests/unit/test_memory_module.py \
tests/unit/test_memory_metric_collector.py \
tests/unit/test_llm_service.py \
tests/unit/test_di_container.py \
tests/unit/test_pipeline.py \
tests/unit/repro/test_agents.py \
tests/unit/repro/test_blueprint.py \
tests/unit/repro/test_memory.py \
tests/unit/repro/test_orchestrator.py \
tests/unit/repro/test_rag.py \
tests/integration/test_eventlog_sqlalchemy.py \
tests/integration/test_crawler_contract_parsers.py \
tests/integration/test_arxiv_connector_fixture.py \
tests/integration/test_reddit_connector_fixture.py \
tests/integration/test_x_importer_fixture.py \
tests/integration/test_repro_deepcode.py \
tests/test_generation_agent.py \
tests/test_repro_planning.py \
tests/test_repro_agent.py \
tests/test_repro_e2e.py \
tests/test_repro_models.py \
tests/e2e/test_api_track_fullstack_offline.py

cd "$(dirname "$0")/../.."

export PYTHONPATH="${PYTHONPATH:-src}"
@jerry609 jerry609 force-pushed the feat/sonarcloud-ci branch from 3243877 to 4d4479f Compare March 15, 2026 05:14
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the project's development and observability capabilities. It introduces a robust SonarCloud CI workflow to maintain high code quality standards across the backend and frontend. Concurrently, it lays the groundwork for a sophisticated, agent-agnostic DeepCode Agent Dashboard, complete with detailed architectural planning, real-time activity monitoring, file change tracking with diff viewing, a Kanban task board, and the foundational data structures for a DAG visualization of agent workflows. These changes collectively aim to provide developers with deeper insights and better control over multi-agent operations.

Highlights

  • SonarCloud CI Integration: A new GitHub Actions workflow has been added to perform SonarCloud analysis on push and pull requests to dev and master branches, ensuring continuous code quality monitoring across both backend and frontend.
  • Comprehensive Planning Documentation: Extensive planning documents have been added, detailing the architecture, concerns, conventions, integrations, technology stack, testing patterns, and phase-by-phase execution for the new v1.2 DeepCode Agent Dashboard, providing a clear roadmap for future development.
  • Real-time Agent Activity Dashboard: A new three-panel agent dashboard UI (/agent-dashboard) has been implemented, featuring a real-time activity feed, per-agent status display, and a tool call timeline, all powered by an extended Zustand store and SSE event stream for live updates.
  • File Change Tracking & Diff Viewer: The agent dashboard now includes a file list panel that tracks agent-modified files with status indicators and an inline diff viewer for inspecting changes, enhancing transparency into agent operations.
  • Kanban Task Board: An interactive Kanban board has been integrated into the agent dashboard, allowing users to visualize tasks by status, with agent identity badges (Claude Code, Codex) and prominent surfacing of Codex-specific error states.
  • Codex Delegation Bridge: Backend event emission for Codex delegation lifecycle (dispatched, accepted, completed, failed) has been implemented, along with a codex-worker.md sub-agent definition for Claude Code to delegate tasks to Codex, enabling multi-agent collaboration.
  • DAG Visualization Data Layer: The data layer for a DAG visualization has been added, including ScoreEdgeEntry for ScoreShareBus data flow and AgentTask.depends_on for task dependencies, along with pure functions to build ReactFlow nodes and edges, laying the groundwork for visual workflow analysis.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • .claude/agents/codex-worker.md
    • Added documentation for the Codex worker sub-agent, detailing its purpose, usage, delegation protocol, and error handling.
  • .gitignore
    • Added entries to ignore coverage reports and SonarCloud analysis files.
  • .planning/PROJECT.md
    • Updated project description to reflect DeepCode as an agent-agnostic dashboard.
    • Added 'v1.2 DeepCode Agent Dashboard' milestone and updated context/constraints.
  • .planning/REQUIREMENTS.md
    • Updated core value to 'agent-agnostic dashboard'.
    • Marked many v1.1 requirements as complete.
    • Added a new 'v1.2 Requirements' section with detailed requirements for agent adapter layer, activity monitoring, chat & control, session management, visualization, and domain integration.
    • Updated 'Out of Scope' and traceability.
  • .planning/ROADMAP.md
    • Added 'v1.2 DeepCode Agent Dashboard' milestone.
    • Updated completion status for Phase 9 and 10 of v1.1.
  • .planning/STATE.md
    • Updated project state to milestone v1.1 'Agent Orchestration Dashboard', status 'verifying'.
    • Updated current focus to 'v1.2 DeepCode Agent Dashboard'.
    • Updated progress metrics and accumulated context with recent decisions.
  • .planning/codebase/ARCHITECTURE.md
    • Added a comprehensive architecture overview document, detailing pattern overview, layers, data flow, key abstractions, entry points, error handling, and cross-cutting concerns.
  • .planning/codebase/CONCERNS.md
    • Added a document outlining codebase concerns, including tech debt, known bugs, security considerations, performance bottlenecks, fragile areas, scaling limits, dependencies at risk, and missing critical features.
  • .planning/codebase/CONVENTIONS.md
    • Added a document detailing coding conventions, including naming patterns, code style, import organization, error handling, logging, comments, function design, module design, file structure & length, and where to add new code.
  • .planning/codebase/INTEGRATIONS.md
    • Added a document detailing external integrations, including APIs & External Services, Data Storage, Authentication & Identity, Monitoring & Observability, CI/CD & Deployment, Environment Configuration, Webhooks & Callbacks, and MCP Server Integration.
  • .planning/codebase/STACK.md
    • Added a document detailing the technology stack, including languages, runtime, package managers, frameworks, testing tools, build & dev tools, key dependencies, configuration, and platform requirements.
  • .planning/codebase/STRUCTURE.md
    • Added a document detailing the codebase structure, including directory layout, directory purposes, key file locations, naming conventions, where to add new code, and special directories.
  • .planning/codebase/TESTING.md
    • Added a document detailing testing patterns, including test framework, test file organization, test structure, mocking, fixtures and factories, coverage, test types, common patterns, and test examples by module.
  • .planning/phases/08-agent-event-vocabulary/08-01-PLAN.md
    • Added a plan to define Python event vocabulary constants and helper functions, and migrate _audit.py.
  • .planning/phases/08-agent-event-vocabulary/08-01-SUMMARY.md
    • Added a summary for Phase 8 Plan 01, detailing the addition of EventType constants, agent_events.py helpers, and _audit.py migration.
  • .planning/phases/08-agent-event-vocabulary/08-02-PLAN.md
    • Added a plan to build the frontend event consumption layer (TypeScript types, parsers, Zustand store, SSE hook, display components, test harness page).
  • .planning/phases/08-agent-event-vocabulary/08-02-SUMMARY.md
    • Added a summary for Phase 8 Plan 02, detailing the frontend agent event consumer layer.
  • .planning/phases/08-agent-event-vocabulary/08-RESEARCH.md
    • Added research for Phase 8, covering event type vocabulary and frontend real-time activity feed.
  • .planning/phases/08-agent-event-vocabulary/08-VALIDATION.md
    • Added validation strategy for Phase 8.
  • .planning/phases/08-agent-event-vocabulary/08-VERIFICATION.md
    • Added verification report for Phase 8.
  • .planning/phases/09-three-panel-dashboard/09-01-PLAN.md
    • Added a plan to extend the agent event data layer with file-change tracking.
  • .planning/phases/09-three-panel-dashboard/09-01-SUMMARY.md
    • Added a summary for Phase 9 Plan 01, detailing the file tracking data layer.
  • .planning/phases/09-three-panel-dashboard/09-02-PLAN.md
    • Added a plan to build the three-panel agent dashboard UI and add sidebar navigation.
  • .planning/phases/09-three-panel-dashboard/09-02-SUMMARY.md
    • Added a summary for Phase 9 Plan 02, detailing the three-panel agent dashboard.
  • .planning/phases/09-three-panel-dashboard/09-RESEARCH.md
    • Added research for Phase 9, covering React/Next.js resizable IDE layout, inline diff rendering, per-task file lists, and Zustand state management.
  • .planning/phases/09-three-panel-dashboard/09-VALIDATION.md
    • Added validation strategy for Phase 9.
  • .planning/phases/09-three-panel-dashboard/09-VERIFICATION.md
    • Added verification report for Phase 9.
  • .planning/phases/10-agent-board-codex-bridge/10-01-PLAN.md
    • Added a plan to add Codex delegation EventType constants, emit delegation lifecycle events, and add Paper2Code overflow routing stub.
  • .planning/phases/10-agent-board-codex-bridge/10-01-SUMMARY.md
    • Added a summary for Phase 10 Plan 01, detailing Codex delegation EventType constants and emission helper.
  • .planning/phases/10-agent-board-codex-bridge/10-02-PLAN.md
    • Added a plan to build the KanbanBoard component with agent badges and Codex error surfacing, and extend the Zustand store and SSE hook for delegation events.
  • .planning/phases/10-agent-board-codex-bridge/10-02-SUMMARY.md
    • Added a summary for Phase 10 Plan 02, detailing KanbanBoard and Codex delegation events.
  • .planning/phases/10-agent-board-codex-bridge/10-03-PLAN.md
    • Added a plan to create the codex-worker.md sub-agent definition and wire KanbanBoard into the agent dashboard page.
  • .planning/phases/10-agent-board-codex-bridge/10-03-SUMMARY.md
    • Added a summary for Phase 10 Plan 03, detailing codex-worker sub-agent and dashboard view toggle.
  • .planning/phases/10-agent-board-codex-bridge/10-RESEARCH.md
    • Added research for Phase 10, covering React Kanban board, Codex delegation, SSE delegation events, Paper2Code overflow, and Codex error surfacing.
  • .planning/phases/10-agent-board-codex-bridge/10-VALIDATION.md
    • Added validation strategy for Phase 10.
  • .planning/phases/11-dag-visualization/11-01-PLAN.md
    • Added a plan to add the data layer and pure logic for DAG visualization.
  • .planning/phases/11-dag-visualization/11-01-SUMMARY.md
    • Added a summary for Phase 11 Plan 01, detailing the DAG data layer.
  • .planning/phases/11-dag-visualization/11-02-PLAN.md
    • Added a plan to build the AgentDagPanel ReactFlow component and integrate it into the agent-dashboard page.
  • .planning/phases/11-dag-visualization/11-RESEARCH.md
    • Added research for Phase 11, covering @xyflow/react real-time DAG visualization.
  • .planning/phases/11-dag-visualization/11-VALIDATION.md
    • Added validation strategy for Phase 11.
  • .planning/research/ARCHITECTURE.md
    • Updated architecture research to focus on v1.2 Agent-Agnostic Dashboard, detailing system overview, adapter layer, data flow, integration points, build order, scaling, and anti-patterns.
  • .planning/research/FEATURES.md
    • Updated feature research to focus on v1.2 DeepCode Agent Dashboard, detailing table stakes, differentiators, anti-features, dependencies, MVP definition, prioritization, and competitor analysis.
  • .planning/research/PITFALLS.md
    • Updated pitfalls research to focus on v1.2 Agent Dashboard, detailing critical pitfalls, technical debt patterns, integration gotchas, performance traps, security mistakes, UX pitfalls, 'looks done but isn't' checklist, and recovery strategies.
  • .planning/research/STACK.md
    • Updated technology stack research to focus on v1.2 Agent Dashboard Additions, detailing new Python/Node.js/Web packages, installation changes, integration with existing code, data migration tooling, alternatives, what NOT to install, agent protocol details, and version compatibility.
  • .planning/research/SUMMARY.md
    • Updated project research summary to focus on v1.2 DeepCode Agent Dashboard.
  • pyproject.toml
    • Added pytest-cov to dev dependencies and configured coverage settings.
  • requirements-ci.txt
    • Added pytest-cov to CI requirements.
  • scripts/ci/run_backend_offline_gates.sh
    • Added a new script to run backend offline tests as part of CI.
  • sonar-project.properties
    • Added SonarCloud project properties for code analysis, specifying source directories, test inclusions, exclusions, and coverage report paths for Python and JavaScript/TypeScript.
  • src/paperbot/api/routes/agent_board.py
    • Added _emit_codex_event helper for emitting Codex delegation events.
    • Added calls to emit codex_dispatched, codex_accepted, codex_failed, and codex_completed events at relevant points in task lifecycle.
  • src/paperbot/application/collaboration/agent_events.py
    • Added make_lifecycle_event and make_tool_call_event helper functions for creating standardized AgentEventEnvelope instances.
  • src/paperbot/application/collaboration/message_schema.py
    • Added EventType class with constants for agent lifecycle, tool calls, file changes, and Codex delegation events, standardizing event types.
  • src/paperbot/mcp/tools/_audit.py
    • Modified to use EventType.TOOL_ERROR and EventType.TOOL_RESULT constants instead of raw strings for event types.
  • src/paperbot/repro/orchestrator.py
    • Added _should_overflow_to_codex function to check for Codex overflow based on an environment variable, providing a stub for delegation logic.
  • tests/unit/test_agent_board_codex_events.py
    • Added unit tests for the _emit_codex_event helper function.
  • tests/unit/test_agent_events_vocab.py
    • Added unit tests for EventType constants, lifecycle events, tool call events, and _audit.py migration.
  • tests/unit/test_codex_overflow.py
    • Added unit tests for the _should_overflow_to_codex function.
  • web/package-lock.json
    • Updated to reflect new dependencies and their versions.
  • web/package.json
    • Added @radix-ui/react-popover, next-auth as dependencies.
    • Added @testing-library/jest-dom, @testing-library/react, @vitest/coverage-v8, jsdom as dev dependencies.
    • Added test:coverage script.
  • web/src/app/agent-dashboard/page.tsx
    • Added a new page for the agent dashboard, integrating SplitPanels, TasksPanel, ActivityFeed, FileListPanel, KanbanBoard, and AgentDagPanel with a view mode toggle.
  • web/src/app/agent-events/page.tsx
    • Added a new debug page for agent events, displaying ActivityFeed, AgentStatusPanel, and ToolCallTimeline.
  • web/src/components/agent-dashboard/AgentDagEdges.tsx
    • Added ScoreFlowEdge custom ReactFlow edge component for visualizing ScoreShareBus data flow.
  • web/src/components/agent-dashboard/AgentDagNodes.tsx
    • Added TaskDagNode custom ReactFlow node component for displaying agent tasks in the DAG.
  • web/src/components/agent-dashboard/AgentDagPanel.test.tsx
    • Added smoke render test for the AgentDagPanel.
  • web/src/components/agent-dashboard/AgentDagPanel.tsx
    • Added AgentDagPanel component for displaying the DAG visualization of agent tasks and data flow.
  • web/src/components/agent-dashboard/FileListPanel.tsx
    • Added FileListPanel component to display agent-modified files, with status indicators and navigation to diff view.
  • web/src/components/agent-dashboard/InlineDiffPanel.tsx
    • Added InlineDiffPanel component to wrap DiffViewer for inline diff display of file changes.
  • web/src/components/agent-dashboard/KanbanBoard.test.tsx
    • Added unit tests for the KanbanBoard component, covering column rendering, agent badges, and error states.
  • web/src/components/agent-dashboard/KanbanBoard.tsx
    • Added KanbanBoard component for displaying tasks in a Kanban layout, including agent identity and Codex error surfacing.
  • web/src/components/agent-dashboard/TasksPanel.tsx
    • Added TasksPanel component for the left rail of the dashboard, showing compact agent status and a run selector.
  • web/src/components/agent-events/ActivityFeed.tsx
    • Added ActivityFeed component to display a real-time scrolling list of agent events.
  • web/src/components/agent-events/AgentStatusPanel.tsx
    • Added AgentStatusPanel component to display per-agent status badges, with a new compact prop for use in narrow layouts.
  • web/src/components/agent-events/ToolCallTimeline.tsx
    • Added ToolCallTimeline component to display a structured log of tool calls.
  • web/src/components/layout/Sidebar.tsx
    • Added 'Agent Dashboard' link to the sidebar navigation.
  • web/src/lib/agent-events/dag.test.ts
    • Added unit tests for DAG builder functions.
  • web/src/lib/agent-events/dag.ts
    • Added pure functions taskStatusToDagStyle, computeTaskDepths, buildDagNodes, buildDagEdges for DAG construction.
  • web/src/lib/agent-events/parsers.test.ts
    • Extended with tests for parseFileTouched, parseCodexDelegation, parseScoreEdge, and deriveHumanSummary for codex events.
  • web/src/lib/agent-events/parsers.ts
    • Added parseFileTouched, parseCodexDelegation, parseScoreEdge functions and extended deriveHumanSummary for codex events.
  • web/src/lib/agent-events/store.test.ts
    • Extended with tests for filesTouched, codexDelegations, kanbanTasks, and scoreEdges store slices.
  • web/src/lib/agent-events/store.ts
    • Extended useAgentEventStore with filesTouched, selectedRunId, selectedFile, codexDelegations, scoreEdges, kanbanTasks state and actions.
  • web/src/lib/agent-events/types.ts
    • Extended with FileChangeStatus, FileTouchedEntry, CodexDelegationEntry, ScoreEdgeEntry types.
  • web/src/lib/agent-events/useAgentEvents.ts
    • Modified to dispatch parseFileTouched, parseCodexDelegation, and parseScoreEdge results from the SSE stream.
  • web/src/lib/store/studio-store.ts
    • Added optional depends_on field to the AgentTask interface.
  • web/vitest.config.ts
    • Configured jsdom environment for component tests and added coverage reporting.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/ci.yml
    • .github/workflows/sonarcloud.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a SonarCloud CI workflow and a substantial set of new features for an agent dashboard. The changes include adding coverage reporting, a shared test script, and extensive new frontend components for visualizing agent activity, such as feeds, Kanban boards, and DAGs. The overall code quality is high, with good test coverage and a clear separation of concerns. I have provided a few suggestions to enhance maintainability and robustness.

I am having trouble creating individual review comments. Click here to see my feedback.

sonar-project.properties (6-7)

medium

The patterns for test files (e.g., web/src/**/*.test.ts) are listed in both sonar.test.inclusions and sonar.exclusions. This is redundant. Files identified as test files via sonar.tests and sonar.test.inclusions are automatically excluded from the main source code analysis for bugs and code smells. Including them in sonar.exclusions as well can make the configuration confusing and harder to maintain.

Removing this redundancy will simplify the configuration without changing its behavior.

sonar.test.inclusions=tests/**/*,web/src/**/*.test.ts,web/src/**/*.test.tsx,web/src/**/*.spec.ts,web/src/**/*.spec.tsx
sonar.exclusions=**/__pycache__/**,**/*.pyc

scripts/ci/run_backend_offline_gates.sh (8-36)

medium

This script hardcodes a long list of test files to be run. This approach can be difficult to maintain, as developers might forget to add new tests to this script, potentially allowing regressions to go undetected by this CI gate.

A more maintainable and declarative approach would be to use pytest markers. You could add a @pytest.mark.offline_gate marker to all the relevant tests and then simplify this script to run all tests with that marker:

python -m pytest -q -m offline_gate "$@"

This way, new tests are automatically included in the suite just by adding the marker, reducing the chance of human error.

web/src/lib/agent-events/store.ts (87-91)

medium

The eviction logic for filesTouched relies on Object.keys() returning keys in insertion order to remove the oldest run. While this is guaranteed for non-integer string keys in modern JavaScript engines (ES2015+), it can be a subtle source of bugs if the code ever runs in an environment that doesn't uphold this guarantee.

For improved robustness, consider maintaining a separate queue (an array) of run_ids to explicitly track insertion order. When the size exceeds the limit, you can dequeue the oldest run_id from this tracking array and use that key to delete the corresponding entry from the filesTouched record. This makes the eviction logic more explicit and less reliant on engine-specific behavior.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (1)
src/paperbot/api/routes/agent_board.py (1)

2430-2453: ⚠️ Potential issue | 🟠 Major

Avoid double-dispatch and premature acceptance states.

If a client calls /tasks/{task_id}/dispatch and then /tasks/{task_id}/execute, this branch emits a second codex_dispatched for the same task. It also appends codex_accepted before build_codex_prompt() and _dispatch_with_step_events() can fail, so the lifecycle can report acceptance for work that never actually started.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/paperbot/api/routes/agent_board.py` around lines 2430 - 2453, The code
emits a second "codex_dispatched" and emits "codex_accepted" prematurely; modify
the flow in agent_board.py around the calls to _emit_codex_event so that you do
not re-emit "codex_dispatched" if the task is already dispatched (check
task.assignee or task.status before emitting) and defer emitting
"codex_accepted" until after build_codex_prompt() and
_dispatch_with_step_events() complete successfully; specifically, remove or
guard the second _emit_codex_event("codex_dispatched", ...) and move the
_emit_codex_event("codex_accepted", ...) call to occur only after
build_codex_prompt() and _dispatch_with_step_events() have succeeded (or add
error handling to avoid reporting acceptance on failure).
🟡 Minor comments (41)
.planning/codebase/STACK.md-8-8 (1)

8-8: ⚠️ Potential issue | 🟡 Minor

Align Python version recommendations.

There's an inconsistency between Line 8 ("Python 3.8+ (target 3.10)") and Line 20 ("Python 3.10+ (recommended), supports 3.8-3.12"). Additionally, the PR objectives mention Python 3.11 in CI and Python 3.9.7 locally.

Consider clarifying the version policy:

  • Minimum supported version (e.g., 3.8)
  • Recommended development version (e.g., 3.10)
  • CI test matrix range (e.g., 3.8-3.12)
📝 Suggested clarification
 **Primary:**
-- Python 3.8+ (target 3.10) - Backend server, agents, data processing
+- Python 3.8-3.12 (recommended 3.10+) - Backend server, agents, data processing

Also applies to: 20-20

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/codebase/STACK.md at line 8, Update STACK.md to remove the
contradictory Python version statements by defining a clear policy: state a
single "Minimum supported version" (e.g., "Python 3.8+"), a single "Recommended
development version" (e.g., "Python 3.10" or 3.11), and a "CI test matrix" range
(e.g., "3.8–3.12"); replace the existing lines "Python 3.8+ (target 3.10)" and
"Python 3.10+ (recommended), supports 3.8-3.12" with the unified policy, and
adjust the PR/CI notes that mention Python 3.11 and 3.9.7 so they match the
chosen minimum/recommended/matrix statements.
.planning/phases/11-dag-visualization/11-01-PLAN.md-207-217 (1)

207-217: ⚠️ Potential issue | 🟡 Minor

Use fenced code blocks consistently in list items.

Line 207 and Line 297 are flagged by MD046. Convert these snippets to explicit fenced blocks (instead of indented block style) to keep markdownlint clean.

Also applies to: 297-304

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/11-dag-visualization/11-01-PLAN.md around lines 207 - 217,
The markdown contains indented code blocks inside list items (notably the block
showing the ScoreEdgeEntry type and the other snippet later) which triggers
MD046; replace those indented blocks with explicit fenced code blocks using
triple backticks (```typescript ... ```) so each list item uses a fenced block,
keeping the language tag (typescript) and preserving the exact code and comments
for ScoreEdgeEntry and the other snippet.
.planning/PROJECT.md-80-80 (1)

80-80: ⚠️ Potential issue | 🟡 Minor

Architecture direction conflicts on Studio replacement.

Line 80 states integration with Studio (not replacement), while Line 124 records a decision to replace Studio with the dashboard. Please align these statements to a single direction.

Also applies to: 124-124

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/PROJECT.md at line 80, Resolve the conflicting architecture
direction by choosing either "integrate with Studio" or "replace Studio with the
dashboard" and make both occurrences consistent: update the phrase at the
"Studio integration" line (currently "Dashboard integrates with existing
Monaco/XTerm, not replaces them") and the decision recorded at the line that
currently says "replace Studio with the dashboard" so they express the same
single direction; ensure any related rationale or consequences elsewhere in the
doc (mentions of Monaco/XTerm or migration/replacement tasks) are adjusted to
match the chosen approach and keep wording consistent (e.g., use "integrates
with Studio" or "replaces Studio with the dashboard") across the document.
.planning/ROADMAP.md-98-100 (1)

98-100: ⚠️ Potential issue | 🟡 Minor

Roadmap status data is internally inconsistent.

Line 98–100 marks phases as complete, but Line 177–179 and Line 193–195 keep related plans unchecked; Line 415–418 also appears column-shifted in the progress table. Please reconcile phase checkboxes, plan completion markers, and table columns so the roadmap remains a reliable source of truth.

Also applies to: 177-179, 193-195, 415-418

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/ROADMAP.md around lines 98 - 100, The roadmap shows inconsistent
completion markers: mark "Phase 9: Three-Panel Dashboard" and "Phase 10: Agent
Board + Codex Bridge" as completed everywhere they appear (add completion dates
where missing) and ensure "Phase 11: DAG Visualization" remains unchecked if
still in progress; then audit and fix duplicate or misaligned entries
referencing these phases elsewhere in the document (search for the exact
headings "Phase 9: Three-Panel Dashboard", "Phase 10: Agent Board + Codex
Bridge", and "Phase 11: DAG Visualization") and update the progress table so its
columns line up with the phase checkboxes (correct any column shifts, remove or
merge duplicated rows, and ensure each phase row has matching checkbox/status
and date columns).
.planning/STATE.md-125-132 (1)

125-132: ⚠️ Potential issue | 🟡 Minor

Pending Todos section is contradictory.

Line 125 says “None.”, but Line 127–132 lists pending roadmap todos. Remove one of them so this section stays unambiguous.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/STATE.md around lines 125 - 132, The "Pending Todos" section
contains a contradictory "None." entry alongside several roadmap items; update
the section so it is unambiguous by removing the incorrect entry (either delete
the "None." line or remove the following roadmap bullet list). Locate the
"Pending Todos" header and adjust the block that currently contains "None." and
the "- [v1.2 roadmap] ..." bullets so only the correct state remains (e.g., keep
the detailed bullets and remove the "None." line, or vice versa).
.planning/codebase/INTEGRATIONS.md-45-47 (1)

45-47: ⚠️ Potential issue | 🟡 Minor

Connector name typo in source list.

Line 45 says “PaperScool”; this should be “PapersCool” to match the connector naming at Line 46 and avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/codebase/INTEGRATIONS.md around lines 45 - 47, Replace the
misspelled connector name "PaperScool" with the correct "PapersCool" in the
INTEGRATIONS.md entry so it matches the connector reference
(paperscool_connector.py); update the heading/line containing "PaperScool" to
"PapersCool" and ensure the surrounding list item text remains unchanged.
.planning/codebase/TESTING.md-100-100 (1)

100-100: ⚠️ Potential issue | 🟡 Minor

Fix duplicated wording.

Line 100 currently reads “Import imports conditionally”; simplify to “Import conditionally inside tests”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/codebase/TESTING.md at line 100, Replace the duplicated phrase
"Import imports conditionally inside tests to support offline runs:" with the
simplified wording "Import conditionally inside tests to support offline runs:";
locate the exact string in the TESTING.md contents (the line currently reading
"Import imports conditionally inside tests to support offline runs:") and update
it to the new phrasing to remove the duplicated word.
.planning/codebase/STRUCTURE.md-7-65 (1)

7-65: ⚠️ Potential issue | 🟡 Minor

Add a language tag to the directory-tree code fence.

Line 7 opens an unlabeled fenced block and triggers MD040. Use text for this tree block.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/codebase/STRUCTURE.md around lines 7 - 65, The code block in the
project structure markdown uses an unlabeled fence which triggers MD040; edit
the opening fence from ``` to ```text in .planning/codebase/STRUCTURE.md so the
directory-tree is explicitly marked as text (update the fenced block that starts
at the directory tree header in the file).
.planning/codebase/TESTING.md-58-68 (1)

58-68: ⚠️ Potential issue | 🟡 Minor

Add a language to the fenced code block.

Line 58 starts an unlabeled fenced block, which triggers MD040. Add a language tag (for example, text) to satisfy lint and improve readability.

Proposed diff
-```
+```text
 tests/
 ├── unit/                    # Fast, isolated, no network/DB
@@
-```
+```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/codebase/TESTING.md around lines 58 - 68, The fenced code block in
TESTING.md that begins with ``` is unlabeled and triggers MD040; change the
opening fence from ``` to ```text (or another appropriate language) so it reads
```text and close it with ``` as now, ensuring the block language tag is added
to the code block that lists the tests/ tree to satisfy the linter and improve
readability.
.planning/codebase/ARCHITECTURE.md-191-193 (1)

191-193: ⚠️ Potential issue | 🟡 Minor

Fix minor grammar for readability.

Use “Set up Docker or E2B sandbox” and “Environment-variable-based API keys” for correct phrasing.

Also applies to: 323-323

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/codebase/ARCHITECTURE.md around lines 191 - 193, Update the
phrasing in the ARCHITECTURE documentation: change the bullet "Setup Docker or
E2B sandbox" to "Set up Docker or E2B sandbox" and replace the phrase
"Environment-variable based API keys" (or similar occurrences) with
"Environment-variable-based API keys" for correct hyphenation; apply these exact
wording fixes to both the Environment Stage section and the other occurrence
around the later mention (the second instance referenced).
.planning/research/STACK.md-117-138 (1)

117-138: ⚠️ Potential issue | 🟡 Minor

Specify a language for the architecture diagram code fence.

Add a fence language (e.g., text) to satisfy markdownlint and keep formatting consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/research/STACK.md around lines 117 - 138, The markdown code fence
containing the architecture diagram (the block starting with "User Browser" and
the ASCII diagram) lacks a fence language and triggers markdownlint; update the
opening triple-backtick to include a language such as text (e.g., change ``` to
```text) so the fence language is specified and formatting is preserved in
.planning/research/STACK.md.
.planning/research/PITFALLS.md-123-125 (1)

123-125: ⚠️ Potential issue | 🟡 Minor

Add language specifiers to fenced code blocks.

These fences should declare a language (likely text) to satisfy markdown linting and improve editor rendering.

Also applies to: 134-138

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/research/PITFALLS.md around lines 123 - 125, Update the fenced
code blocks that currently contain the snippet "data: {...}\n\n" so the opening
triple backticks include a language specifier (e.g., change ``` to ```text) to
satisfy markdown linting and improve editor rendering; apply the same change to
the other similar fenced block referenced in the comment (the one around the
subsequent occurrence).
.planning/phases/10-agent-board-codex-bridge/10-01-SUMMARY.md-35-35 (1)

35-35: ⚠️ Potential issue | 🟡 Minor

Wrap underscore-heavy identifiers in backticks consistently.

Names like _emit_codex_event and _get_event_log_from_container should be code-formatted everywhere to avoid markdown emphasis parsing/lint issues.

Also applies to: 48-50

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/10-agent-board-codex-bridge/10-01-SUMMARY.md at line 35,
The markdown uses underscore-heavy identifiers without code formatting which
triggers emphasis parsing; update the summary to wrap all occurrences of
identifiers like `_emit_codex_event`, `_get_event_log_from_container`, and the
`CODEX_*` EventType constants in backticks so they render as inline code
everywhere (including the other mentions later in the summary) to avoid
markdown/lint issues.
.planning/research/ARCHITECTURE.md-575-575 (1)

575-575: ⚠️ Potential issue | 🟡 Minor

Use “agent-agnostic CLIs” instead of “agent-agnostic CLI interfaces.”

This is redundant wording; shortening improves clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/research/ARCHITECTURE.md at line 575, Replace the phrase
"agent-agnostic CLI interfaces" with the shorter "agent-agnostic CLIs" in the
ARCHITECTURE.md entry that references the "Agent Client Protocol architecture"
link so the bullet reads "... — JSON-RPC 2.0 over stdin/stdout as emerging
standard for agent-agnostic CLIs".
.planning/research/SUMMARY.md-241-241 (1)

241-241: ⚠️ Potential issue | 🟡 Minor

Tighten redundant phrasing.

“agent-agnostic CLI interfaces” is redundant; “agent-agnostic CLIs” is clearer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/research/SUMMARY.md at line 241, Edit the bullet text that
currently reads "JSON-RPC 2.0 over stdin/stdout as emerging standard for
agent-agnostic CLI interfaces" (the Agent Client Protocol architecture line) to
remove redundant phrasing by replacing "agent-agnostic CLI interfaces" with
"agent-agnostic CLIs" so the line reads "...as emerging standard for
agent-agnostic CLIs".
.planning/phases/08-agent-event-vocabulary/08-RESEARCH.md-69-92 (1)

69-92: ⚠️ Potential issue | 🟡 Minor

Add a language tag to the fenced file-structure block.

Use a text fence to satisfy markdownlint and improve readability in rendered docs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/08-agent-event-vocabulary/08-RESEARCH.md around lines 69 -
92, The fenced file-structure block in
.planning/phases/08-agent-event-vocabulary/08-RESEARCH.md is missing a language
tag; update the opening fence that currently uses ``` to ```text so the file
tree block (the src/paperbot/... and web/src/... tree shown in the diff) is
marked as plain text and satisfies markdownlint and improves rendered
readability.
tests/unit/test_codex_overflow.py-1-5 (1)

1-5: ⚠️ Potential issue | 🟡 Minor

Update stale TDD status text.

The module docstring says these tests should fail until implementation exists, which is no longer true and can confuse future maintainers.

✏️ Proposed fix
-"""Unit tests for _should_overflow_to_codex overflow routing stub.
-
-TDD: These tests define the required API. They will fail (RED) until
-_should_overflow_to_codex is implemented in repro/orchestrator.py.
-"""
+"""Unit tests for _should_overflow_to_codex environment-driven routing."""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test_codex_overflow.py` around lines 1 - 5, Update the module
docstring in tests/unit/test_codex_overflow.py to remove the stale TDD sentence
that says the tests will fail until _should_overflow_to_codex is implemented;
instead state that the tests validate the behavior of _should_overflow_to_codex
(implementation located in repro/orchestrator.py) or simply provide a brief
description of what the tests cover so future maintainers aren't misled.
web/src/components/agent-dashboard/InlineDiffPanel.tsx-21-27 (1)

21-27: ⚠️ Potential issue | 🟡 Minor

Add an accessible label to the icon-only back button.

Line 21 renders a button with only an icon; please add aria-label so assistive tech can identify it.

♿ Proposed fix
         <Button
           variant="ghost"
           size="icon"
           className="h-7 w-7 shrink-0"
           onClick={onBack}
+          aria-label="Back to file list"
+          title="Back"
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-dashboard/InlineDiffPanel.tsx` around lines 21 - 27,
The icon-only back Button in InlineDiffPanel lacks an accessible name; update
the Button component rendering (the Button wrapping ArrowLeft in
InlineDiffPanel) to include an aria-label (e.g., aria-label="Back" or
aria-label="Go back") so assistive technologies can identify it while keeping
onClick={onBack} intact.
web/src/components/agent-dashboard/AgentDagPanel.test.tsx-58-95 (1)

58-95: ⚠️ Potential issue | 🟡 Minor

This test doesn't actually verify studio-store precedence.

Both stores are populated here, but the only assertion is that ReactFlow rendered. That would still pass if the component incorrectly kept using eventKanbanTasks. Assert the selected task IDs/nodes, or spy on buildDagNodes, so this case proves the precedence rule.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-dashboard/AgentDagPanel.test.tsx` around lines 58 -
95, The test currently only checks that ReactFlow rendered but not that
studioStore tasks take precedence; update the test to assert that the studio
task is the one used by AgentDagPanel: either spy/mock the buildDagNodes
function and assert it was called with useStudioStore state (agentTasks) rather
than useAgentEventStore. Alternatively, inspect rendered nodes/text from the
AgentDagPanel (e.g., look for the studio task id/title or node DOM that
corresponds to "studio-task-1"/"Studio Task") and assert those are present while
the event task is not, ensuring useStudioStore wins over useAgentEventStore.
tests/unit/test_agent_board_codex_events.py-33-46 (1)

33-46: ⚠️ Potential issue | 🟡 Minor

Use concrete fakes here instead of MagicMock.

These helpers back the core payload assertions in this file, but MagicMock makes the doubles more permissive than the real task/session/container shapes. Small fake classes (or types.SimpleNamespace for the container) would keep the tests stricter and easier to reason about.

As per coding guidelines "Use stub/fake classes (e.g., _FakeLLMService) rather than unittest.mock for test doubles in Python tests".

Also applies to: 61-62

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test_agent_board_codex_events.py` around lines 33 - 46, Replace
the overly-permissive MagicMock-based helpers with concrete fakes: change
_make_task to return a small stub class or types.SimpleNamespace with explicit
attributes id, title, assignee (to mirror AgentTask) and change _make_session to
return a stub with session_id (to mirror BoardSession); likewise replace the
other MagicMock helpers around lines 61-62 with concrete fakes so tests assert
the real shape and fail on unexpected attribute access. Ensure the helper names
_make_task and _make_session remain unchanged so callers keep working.
web/src/app/agent-events/page.tsx-29-34 (1)

29-34: ⚠️ Potential issue | 🟡 Minor

Use a breakpoint-aware divider here.

With grid-cols-1, the first pane stacks above the second, so the always-on border-r renders on the outside edge instead of between sections. Switch that separator to a bottom border on mobile and a right border from md upward.

Suggested tweak
-          <div className="border-r border-gray-800 overflow-hidden flex flex-col min-h-0">
+          <div className="border-b border-gray-800 overflow-hidden flex flex-col min-h-0 md:border-b-0 md:border-r">
             <ActivityFeed />
           </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/agent-events/page.tsx` around lines 29 - 34, The vertical divider
is always applied via the "border-r" on the left pane, which visually places the
divider on the outside when the grid stacks; update the left pane wrapping div
that contains ActivityFeed so the separator is breakpoint-aware: replace the
unconditional right border with a bottom border for small screens and switch to
a right border at md+ (e.g., use responsive classes like border-b md:border-r
and disable the bottom border at md with md:border-b-0), keeping the existing
color class (border-gray-800) and other layout classes on the same element (the
div that currently wraps ActivityFeed).
.planning/phases/11-dag-visualization/11-VALIDATION.md-22-23 (1)

22-23: ⚠️ Potential issue | 🟡 Minor

The documented fast path skips the new panel smoke test.

Both the quick-run command and the per-commit sampling command only cover src/lib/agent-events/, but this phase also depends on src/components/agent-dashboard/AgentDagPanel.test.tsx. Following the doc as written can leave the DAG UI red while validation still looks green.

Suggested update
-| **Quick run command** | `cd web && npm test -- --reporter=verbose src/lib/agent-events/` |
+| **Quick run command** | `cd web && npm test -- --reporter=verbose src/lib/agent-events/ src/components/agent-dashboard/AgentDagPanel.test.tsx` |
...
-- **After every task commit:** Run `cd web && npm test -- src/lib/agent-events/`
+- **After every task commit:** Run `cd web && npm test -- src/lib/agent-events/ src/components/agent-dashboard/AgentDagPanel.test.tsx`

Also applies to: 30-31, 44-44

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/11-dag-visualization/11-VALIDATION.md around lines 22 - 23,
The quick-run and per-commit test commands in the validation doc only target
src/lib/agent-events/ and therefore skip the new DAG panel smoke test; update
the commands so they also include
src/components/agent-dashboard/AgentDagPanel.test.tsx (or the containing folder
src/components/agent-dashboard/) so the fast-path and sampling commands run both
src/lib/agent-events/ and the AgentDagPanel test; apply the same change to the
other occurrences referenced (lines near the other two affected sections) so all
quick/full commands include the DAG panel test.
web/src/components/agent-dashboard/AgentDagPanel.test.tsx-26-100 (1)

26-100: ⚠️ Potential issue | 🟡 Minor

Reset both Zustand stores in setup/teardown, not inline.

This suite mutates module-level stores and only clears them on the happy path of two tests. If an assertion fails before cleanup, later cases inherit stale kanbanTasks/agentTasks and become order-dependent.

Suggested cleanup pattern
-import { describe, expect, it, vi } from "vitest"
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
 ...
 describe("AgentDagPanel", () => {
+  const resetStores = () => {
+    useAgentEventStore.setState({ kanbanTasks: [], scoreEdges: [] })
+    useStudioStore.setState({ agentTasks: [] })
+  }
+
+  beforeEach(() => {
+    resetStores()
+  })
+
+  afterEach(() => {
+    resetStores()
+  })
+
   it("renders empty state when no tasks", () => {
     render(<AgentDagPanel />)
     expect(screen.getByText(/No tasks to visualize/)).toBeTruthy()
   })
 ...
-
-    // Reset store after test
-    useAgentEventStore.setState({ kanbanTasks: [] })
   })
 ...
-
-    // Reset stores after test
-    useAgentEventStore.setState({ kanbanTasks: [] })
-    useStudioStore.setState({ agentTasks: [] })
   })
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-dashboard/AgentDagPanel.test.tsx` around lines 26 -
100, The tests mutate module-level Zustand stores (useAgentEventStore,
useStudioStore) inline and only reset them in some tests; refactor to reset both
stores in setup/teardown so tests are isolated: add a beforeEach/afterEach pair
(or afterEach alone) that calls useAgentEventStore.setState({ kanbanTasks: [] })
and useStudioStore.setState({ agentTasks: [] }) (and any other default keys)
before/after each spec, and remove the inline resets from the individual it
blocks (so functions like the ReactFlow assertions no longer rely on test
order).
.planning/REQUIREMENTS.md-273-297 (1)

273-297: ⚠️ Potential issue | 🟡 Minor

v1.2 traceability rows are detached from the main table.

After Line [259] (**Coverage:**), the table context is broken, so Lines [273]-[297] may render as plain text rows instead of part of the traceability matrix. Consider moving these rows into the main table section (or add a new table header block).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/REQUIREMENTS.md around lines 273 - 297, The traceability rows
starting with identifiers like ADAPT-01, MONIT-01, CTRL-01, SESS-01, VIS-01,
DOMAIN-01 etc. are detached because the table context is broken after the
"**Coverage:**" line; fix by reinserting these rows into the main Markdown table
(or add a proper table header block immediately before them) so they render as
table rows again—locate the "**Coverage:**" marker and either move the
ADAPT-/MONIT-/CTRL-/SESS-/VIS-/DOMAIN- rows directly under the existing table
header or prepend the missing header row (e.g., | ID | Phase | Status |) before
these rows.
.claude/agents/codex-worker.md-53-74 (1)

53-74: ⚠️ Potential issue | 🟡 Minor

Fix markdownlint issues in code spans and fenced blocks.

There are small lint violations: trailing space inside the data: code span and missing language tags on the sample fenced blocks.

📝 Suggested fix
-SSE events are newline-delimited JSON prefixed with `data: `. Watch for:
+SSE events are newline-delimited JSON prefixed with `data:`. Watch for:
@@
-```
+```text
 Codex completed task <TASK_ID>.
 Output summary: <brief description of what was generated/fixed>
 Generated files: <comma-separated list if available>
 Codex output: <first 500 chars of codex_output>

@@
- +text
Codex failed task <TASK_ID>.
Reason: <reason_code from codex_diagnostics, or lastError, or "unknown">
Recommendation: <retry with simplified scope | escalate to Claude Code | manual intervention>

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/agents/codex-worker.md around lines 53 - 74, Fix the markdownlint
issues by removing the trailing space inside the inline code span `data:` in the
SSE description and add explicit language tags to the example fenced blocks
under "Step 4 — Report Result" (use ```text for both success and failure
blocks); update the `data:` inline code and replace the three untitled fenced
blocks with fenced blocks labeled `text` so linting passes while preserving the
exact sample content in the Codex success/failure messages.
.planning/research/FEATURES.md-53-53 (1)

53-53: ⚠️ Potential issue | 🟡 Minor

Tighten two flagged sentences for readability.

Line 53 has awkward phrasing (“Requires ordered…”), and Line 132 is a sentence fragment. Please rewrite both into complete, direct sentences.

Also applies to: 132-132

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/research/FEATURES.md at line 53, Edit the "Session replay" entry
to replace the awkward fragment "Requires ordered, timestamped event storage."
with a complete sentence such as "This feature requires storing ordered,
timestamped events for each run_id to enable accurate replay." Also fix the
sentence fragment at line 132 by rewriting it into a complete, direct sentence
(e.g., expand the fragment into a full sentence that explains the requirement or
behavior). Locate the text around the "Session replay" header and any occurrence
of `run_id` or "event log" to apply these changes.
.planning/phases/10-agent-board-codex-bridge/10-02-PLAN.md-189-189 (1)

189-189: ⚠️ Potential issue | 🟡 Minor

Convert indented code blocks to fenced blocks and add language tags.

Line 189 and Line 281 should be fenced blocks (not indented style), and the fenced block at Line 328 should declare a language.

Also applies to: 281-281, 328-328

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/10-agent-board-codex-bridge/10-02-PLAN.md at line 189, In
the "GREEN phase:" section there are indented code blocks that must be converted
to fenced code blocks and the fenced block later must declare a language;
replace the indented-style blocks near the "GREEN phase:" heading (the two
instances currently using indentation) with triple-backtick fenced blocks and
add an appropriate language tag (e.g., ```bash or ```text) to the fenced block
currently missing a language so all three blocks use fenced syntax and include
language identifiers for proper highlighting.
.planning/phases/08-agent-event-vocabulary/08-01-PLAN.md-45-45 (1)

45-45: ⚠️ Potential issue | 🟡 Minor

Fix emphasis marker spacing to satisfy markdownlint MD037.

Line 45, Line 117, Line 119, Line 142, and Line 153 contain emphasis syntax with internal spacing. Please normalize those markers so lint stays clean in CI.

Also applies to: 117-117, 119-119, 142-142, 153-153

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/08-agent-event-vocabulary/08-01-PLAN.md at line 45, Several
emphasis markers in the markdown use internal spacing (e.g., "* Establish the
backend contracts *", "* make_lifecycle_event *", etc.), which triggers
markdownlint MD037; fix by removing the spaces between the asterisks/underscores
and the text so emphasis is written as "*Establish the backend contracts*",
"*make_lifecycle_event*", "*make_tool_call_event*", etc.; scan the file for
other instances of "* text *" or "_ text _" and normalize them to "*text*" or
"_text_" to satisfy the linter.
.planning/phases/08-agent-event-vocabulary/08-01-SUMMARY.md-111-112 (1)

111-112: ⚠️ Potential issue | 🟡 Minor

Align emphasis style with configured markdownlint rule.

Line 111 and Line 112 use asterisk emphasis, but the lint rule expects underscore emphasis.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/08-agent-event-vocabulary/08-01-SUMMARY.md around lines 111
- 112, Replace the asterisk-based emphasis with underscore emphasis for the two
lines containing "Phase: 08-agent-event-vocabulary" and "Completed: 2026-03-15"
so they use _Phase: 08-agent-event-vocabulary_ and _Completed: 2026-03-15_;
update both occurrences to match the configured markdownlint rule for emphasis.
.planning/research/FEATURES.md-24-24 (1)

24-24: ⚠️ Potential issue | 🟡 Minor

Normalize table and code-fence syntax for markdownlint compliance.

Please fix Line 24 table pipe formatting, ensure blank lines around the affected tables (Line 29, Line 37, Line 46), and add a language identifier to the fenced block at Line 57.

Also applies to: 29-29, 37-37, 46-46, 57-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/research/FEATURES.md at line 24, Fix the malformed Markdown table
row by normalizing pipe spacing and ensuring each table header and row uses
leading and trailing pipes consistently (correct the broken pipe in the table
header row), add a blank line before and after each of the three Markdown tables
so they are separated from surrounding text, and add an explicit language
identifier to the fenced code block that currently uses only ``` (e.g., ```bash
or ```json as appropriate) so the fence is recognized by markdownlint.
.planning/phases/10-agent-board-codex-bridge/10-03-PLAN.md-132-132 (1)

132-132: ⚠️ Potential issue | 🟡 Minor

Normalize code-fence formatting in this plan doc.

Line 132 should use fenced block style (not indented), and Line 199 should include a language tag on the fenced block.

Also applies to: 199-199

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/10-agent-board-codex-bridge/10-03-PLAN.md at line 132, The
plan document currently uses an indented code block for the step "2. Update
`web/src/app/agent-dashboard/page.tsx`:"; replace that indented block with a
fenced triple-backtick code fence and ensure the later fenced block near the
same section includes a language tag (e.g., ```tsx) to enable syntax
highlighting; locate the block by the line that reads "2. Update
`web/src/app/agent-dashboard/page.tsx`:" and convert its indented code-fence to
``` and add the appropriate language tag to the fenced block referenced later in
the section.
web/src/lib/agent-events/parsers.ts-34-60 (1)

34-60: ⚠️ Potential issue | 🟡 Minor

Avoid String(unknown) for user-facing fields to prevent [object Object] text.

Several summary/parser fields are coerced with String(...). If backend payloads drift, UI can show [object Object] instead of readable text. Prefer strict typeof value === "string" checks with explicit fallbacks.

Also applies to: 89-96, 155-158

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/parsers.ts` around lines 34 - 60, The parser
currently coerces many user-facing fields with String(...) (e.g., in the
branches for "agent_started", "agent_error",
"tool_result"/"tool_error"/"tool_call", "codex_dispatched", "codex_accepted",
"codex_completed", "codex_failed"), which can render "[object Object]" when
payloads are non-string; update each place to only use the value if typeof value
=== "string", otherwise fall back to a safe representation (empty string or
JSON.stringify(value) for debugging) and for summaries truncate the final
string; for example replace String(payload.result_summary ?? "") with (typeof
payload.result_summary === "string" ? payload.result_summary :
(payload.result_summary ? JSON.stringify(payload.result_summary) : "")) and
apply the same pattern for raw.agent_name, payload.detail, payload.assignee,
payload.task_title, payload.reason_code, and payload.files_generated checks so
the UI always receives a readable string.
.planning/phases/09-three-panel-dashboard/09-RESEARCH.md-75-89 (1)

75-89: ⚠️ Potential issue | 🟡 Minor

Add a language identifier to the fenced code block.

The file-structure block starts as plain triple backticks; markdownlint expects a language tag (MD040).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-RESEARCH.md around lines 75 -
89, The fenced code block in
.planning/phases/09-three-panel-dashboard/09-RESEARCH.md is missing a language
identifier (violates MD040); update the opening fence for the tree block (the
triple backticks that precede the web/src/ tree) to include a language tag such
as "text" (e.g., change ``` to ```text) so markdownlint recognizes it as a code
block; no other content changes are needed.
.planning/phases/08-agent-event-vocabulary/08-02-SUMMARY.md-146-147 (1)

146-147: ⚠️ Potential issue | 🟡 Minor

Use underscore emphasis markers to satisfy MD049.

Line 146 and Line 147 use *...*; this file’s lint config expects underscore emphasis.

Suggested fix
-*Phase: 08-agent-event-vocabulary*
-*Completed: 2026-03-15*
+_Phase: 08-agent-event-vocabulary_
+_Completed: 2026-03-15_
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/08-agent-event-vocabulary/08-02-SUMMARY.md around lines 146
- 147, Replace the asterisk emphasis markers with underscores to satisfy MD049:
change "*Phase: 08-agent-event-vocabulary*" to "_Phase:
08-agent-event-vocabulary_" and "*Completed: 2026-03-15*" to "_Completed:
2026-03-15_"; update the two literal lines containing "Phase:
08-agent-event-vocabulary" and "Completed: 2026-03-15" accordingly.
.planning/phases/09-three-panel-dashboard/09-01-SUMMARY.md-126-127 (1)

126-127: ⚠️ Potential issue | 🟡 Minor

Use underscore emphasis to match markdownlint style rules.

Line 126 and Line 127 use *...*, but lint expects _..._ (MD049). This is low-impact but keeps docs lint-clean.

Suggested fix
-*Phase: 09-three-panel-dashboard*
-*Completed: 2026-03-15*
+_Phase: 09-three-panel-dashboard_
+_Completed: 2026-03-15_
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-01-SUMMARY.md around lines 126
- 127, Replace the inline emphasis markers using asterisks with underscores to
satisfy MD049: change "*Phase: 09-three-panel-dashboard*" to "_Phase:
09-three-panel-dashboard_" and "*Completed: 2026-03-15*" to "_Completed:
2026-03-15_" in the 09-three-panel-dashboard summary so both lines use
underscore emphasis instead of asterisks.
.planning/phases/11-dag-visualization/11-02-PLAN.md-161-246 (1)

161-246: ⚠️ Potential issue | 🟡 Minor

Convert indented code examples to fenced blocks for markdownlint compliance.

In these task sections, markdownlint flags indented code style (MD046). Please use fenced code blocks (ts/tsx or bash) for the snippets to avoid lint noise.

Also applies to: 264-303

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/11-dag-visualization/11-02-PLAN.md around lines 161 - 246,
Convert the indented code examples in the "Task 1: AgentDagNodes, AgentDagEdges,
AgentDagPanel components + smoke test" section into fenced code blocks (e.g.,
```tsx```, ```ts```, or ```bash```) so markdownlint MD046 is satisfied; update
every snippet that describes the implementations for AgentDagNodes.tsx,
AgentDagEdges.tsx, AgentDagPanel.tsx and the AgentDagPanel.test.tsx mock
snippets to use fenced blocks instead of indented code, preserving the exact
snippet content and language hints.
.planning/phases/09-three-panel-dashboard/09-RESEARCH.md-12-18 (1)

12-18: ⚠️ Potential issue | 🟡 Minor

Escape literal pipes inside table cells to avoid broken column parsing.

Line 14 includes unescaped | in cell content (tasks | activity | files), which breaks table structure (MD056/MD055).

Suggested fix
-| DASH-01 | User can view agent orchestration in a three-panel IDE layout (tasks | activity | files) | `SplitPanels` component already implements a three-panel `ResizablePanelGroup` pattern with `localStorage` persistence; Phase 9 reuses it with agent-specific content |
+| DASH-01 | User can view agent orchestration in a three-panel IDE layout (tasks \| activity \| files) | `SplitPanels` component already implements a three-panel `ResizablePanelGroup` pattern with `localStorage` persistence; Phase 9 reuses it with agent-specific content |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-RESEARCH.md around lines 12 -
18, The table row for ID DASH-01 contains unescaped pipe characters in the cell
"tasks | activity | files" which breaks Markdown table parsing; edit the DASH-01
row in .planning/phases/09-three-panel-dashboard/09-RESEARCH.md (look for the
line containing "DASH-01") and escape each literal pipe as \| (or wrap the
entire cell in backticks) so the cell renders as a single column value and the
table column alignment is preserved.
.planning/phases/10-agent-board-codex-bridge/10-RESEARCH.md-207-207 (1)

207-207: ⚠️ Potential issue | 🟡 Minor

Normalize code block style to fenced blocks.

These sections trigger markdownlint MD046 due to indented code block formatting. Please convert to fenced blocks.

Also applies to: 297-297

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/10-agent-board-codex-bridge/10-RESEARCH.md at line 207,
Replace indented code blocks that trigger markdownlint MD046 with fenced code
blocks (```lang or ``` when no language) in the affected markdown sections
(notably around the blocks referenced at lines 207 and 297); ensure you convert
each leading-four-space indented snippet to a fenced block, preserve any
language hint or inline content, and keep surrounding blank lines so rendering
and linting pass (search for the indented blocks in this file and swap them to
triple-backtick fenced blocks).
.planning/phases/09-three-panel-dashboard/09-01-PLAN.md-306-314 (1)

306-314: ⚠️ Potential issue | 🟡 Minor

Code block style is inconsistent with lint rules.

This section also triggers markdownlint MD046 (indented code block). Please switch to fenced code block formatting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-01-PLAN.md around lines 306 -
314, Replace the indented code block in the "Step 2 — Python test
(test_agent_events_vocab.py)" section with a fenced code block using triple
backticks and a language tag (```python) to satisfy lint rule MD046; keep the
test function name test_file_change_event_type and its assertions against
EventType.FILE_CHANGE unchanged and place the entire function inside the fenced
block.
.planning/phases/10-agent-board-codex-bridge/10-01-PLAN.md-171-178 (1)

171-178: ⚠️ Potential issue | 🟡 Minor

Convert this indented code section to fenced format.

markdownlint MD046 reports code-block style mismatch here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/10-agent-board-codex-bridge/10-01-PLAN.md around lines 171
- 178, The indented code block under the GREEN phase should be converted to a
fenced code block to satisfy markdownlint MD046; replace the indented snippet
that adds constants to the EventType class (the lines referencing EventType and
the constants CODEX_DISPATCHED, CODEX_ACCEPTED, CODEX_COMPLETED, CODEX_FAILED)
with a fenced code block (triple backticks, e.g., ```python ... ```) containing
those constant definitions and keep the surrounding comment `# --- Codex
delegation events (Phase 10 / CDX-03) ---` intact.
.planning/phases/09-three-panel-dashboard/09-01-PLAN.md-183-197 (1)

183-197: ⚠️ Potential issue | 🟡 Minor

Use fenced code blocks instead of indented blocks.

markdownlint MD046 is triggered here. Convert these indented code sections to fenced blocks for lint compliance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-01-PLAN.md around lines 183 -
197, The markdown uses indented code blocks which triggers markdownlint MD046;
replace each indented code section in the
".planning/phases/09-three-panel-dashboard/09-01-PLAN.md" fragment (notably the
"Step 1 — Types (types.ts APPEND)" snippet that shows declarations like
FileChangeStatus, FileTouchedEntry and the surrounding ToolCallEntry reference)
with fenced code blocks using appropriate language tags (e.g., ```typescript)
and proper opening/closing fences so the block is fenced rather than indented,
ensuring all examples follow fenced-block formatting to satisfy MD046.
🧹 Nitpick comments (10)
web/vitest.config.ts (1)

13-16: Consider adding *.spec.ts(x) patterns to environmentMatchGlobs for future consistency.

Currently, no component *.spec.* files exist in the codebase, but coverage.exclude already covers both test and spec patterns. Adding the corresponding jsdom mappings preemptively ensures parity and avoids surprises if spec files are introduced later.

✨ Optional tweak
     environmentMatchGlobs: [
       ["src/components/**/*.test.tsx", "jsdom"],
       ["src/components/**/*.test.ts", "jsdom"],
+      ["src/components/**/*.spec.tsx", "jsdom"],
+      ["src/components/**/*.spec.ts", "jsdom"],
     ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/vitest.config.ts` around lines 13 - 16, Update the environmentMatchGlobs
array so it includes spec file patterns as well as test patterns: add entries
for "src/components/**/*.spec.tsx" and "src/components/**/*.spec.ts" mapped to
"jsdom" alongside the existing "src/components/**/*.test.tsx" and
"src/components/**/*.test.ts" entries (modify the environmentMatchGlobs constant
in vitest.config.ts).
.github/workflows/sonarcloud.yml (1)

18-20: Scope permissions per job, not workflow-wide.

Line 18–20 grants permissions globally, including jobs that do not need them. Move these to job level to keep least-privilege boundaries tighter.

Proposed diff
-permissions:
-  contents: read
-  pull-requests: read
-
 jobs:
   scan:
     name: SonarCloud Scan
+    permissions:
+      contents: read
+      pull-requests: read
     if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
@@
   fork_notice:
     name: Fork PR notice
+    permissions:
+      contents: read
     if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/sonarcloud.yml around lines 18 - 20, The workflow
currently sets a global permissions block ("permissions" with "contents: read"
and "pull-requests: read") which is too broad; remove this top-level
"permissions" section and instead add minimal per-job permission blocks under
each job (e.g., under "jobs: <job_name>: permissions:") granting only the needed
scopes (for example "contents: read" or "pull-requests: read" where required) so
each job limits privileges to what it uses.
.planning/phases/11-dag-visualization/11-01-PLAN.md (1)

253-253: Use repo-relative test commands instead of machine-specific paths.

Line 253, Line 335, and Line 351–352 hardcode /home/master1/PaperBot/..., which is brittle for other contributors and CI docs reuse.

Proposed diff
-    <automated>cd /home/master1/PaperBot/web && npm test -- --reporter=verbose src/lib/agent-events/parsers.test.ts src/lib/agent-events/store.test.ts</automated>
+    <automated>cd web && npm test -- --reporter=verbose src/lib/agent-events/parsers.test.ts src/lib/agent-events/store.test.ts</automated>
@@
-    <automated>cd /home/master1/PaperBot/web && npm test -- --reporter=verbose src/lib/agent-events/dag.test.ts</automated>
+    <automated>cd web && npm test -- --reporter=verbose src/lib/agent-events/dag.test.ts</automated>
@@
-All agent-events tests pass: `cd /home/master1/PaperBot/web && npm test -- --reporter=verbose src/lib/agent-events/`
-Full web test suite passes: `cd /home/master1/PaperBot/web && npm test`
+All agent-events tests pass: `cd web && npm test -- --reporter=verbose src/lib/agent-events/`
+Full web test suite passes: `cd web && npm test`

Also applies to: 335-335, 351-352

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/11-dag-visualization/11-01-PLAN.md at line 253, The
automated test command hardcodes a machine-specific absolute path (the string
"cd /home/master1/PaperBot/web && npm test ..."), which is brittle; update the
command to be repo-relative (e.g., "cd web && npm test -- --reporter=verbose
src/lib/agent-events/parsers.test.ts src/lib/agent-events/store.test.ts" or use
"npm --prefix web test ...") so contributors and CI can run tests without the
/home/master1 path; change the occurrences matching that automated command on
the affected lines accordingly.
.planning/phases/09-three-panel-dashboard/09-02-PLAN.md (1)

218-218: Avoid machine-specific absolute paths in verification commands.

Using /home/master1/PaperBot/web makes the plan less portable. Prefer repository-relative commands (cd web && ...) so others and CI runners can execute verbatim.

Suggested doc edit
- <automated>cd /home/master1/PaperBot/web && npx tsc --noEmit 2>&1 | tail -20</automated>
+ <automated>cd web && npx tsc --noEmit 2>&1 | tail -20</automated>

- <automated>cd /home/master1/PaperBot/web && npx tsc --noEmit 2>&1 | tail -10 && npx next build 2>&1 | tail -20</automated>
+ <automated>cd web && npx tsc --noEmit 2>&1 | tail -10 && npx next build 2>&1 | tail -20</automated>

- <automated>cd /home/master1/PaperBot/web && npx tsc --noEmit 2>&1 | tail -5</automated>
+ <automated>cd web && npx tsc --noEmit 2>&1 | tail -5</automated>

Also applies to: 271-271, 308-308

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-02-PLAN.md at line 218, The
verification command uses a machine-specific absolute path
"/home/master1/PaperBot/web" in the automated block; change the automated
command string to a repository-relative form like "cd web && npx tsc --noEmit
2>&1 | tail -20" (and make the equivalent edits for the other occurrences noted
around lines 271 and 308) so the plan is portable for other developers and CI
runners.
tests/unit/test_codex_overflow.py (1)

10-56: Consider parametrizing these env-value cases.

The tests are good, but this block can be made leaner and easier to extend with pytest.mark.parametrize.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test_codex_overflow.py` around lines 10 - 56, These tests repeat
the same pattern for different env values; refactor to use
pytest.mark.parametrize to reduce duplication: create a single test function
(e.g., test_overflow_env_values) annotated with `@pytest.mark.parametrize` that
supplies tuples of (env_value, expected_bool) covering the cases "", None
(delete env), "true","1","0","yes","on", call monkeypatch.setenv or
monkeypatch.delenv based on the param, import
paperbot.repro.orchestrator._should_overflow_to_codex once at the top of the
test, and assert the returned value equals expected_bool; this keeps behavior
identical while making future cases easy to add.
web/src/components/agent-dashboard/AgentDagEdges.tsx (1)

31-46: Prefer semantic theme tokens over hardcoded edge colors.

Using fixed hex/white values here can look out-of-place across themes. Prefer Tailwind semantic classes (bg-background, border-border, semantic text/stroke colors).

As per coding guidelines Keep Tailwind CSS usage consistent with nearby components in TypeScript/React.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-dashboard/AgentDagEdges.tsx` around lines 31 - 46,
The SVG edge currently uses hardcoded colors (stroke="#8b5cf6", fill="none") and
the label div uses fixed color classes (text-purple-500, bg-white,
border-purple-200); replace these with semantic Tailwind tokens to follow
theming conventions in AgentDagEdges: change the SVG path to remove the literal
stroke value and instead apply a className with a semantic stroke token (e.g.,
className="stroke-primary" or your project's semantic stroke class) and keep
fill="none" or replace with a semantic fill token if needed, and update the
label div className to use semantic classes like text-primary, bg-background,
border-border while preserving layout props (transform using labelX/labelY,
pointerEvents, markerEnd usage) so theming is consistent across light/dark
modes.
web/src/components/agent-events/AgentStatusPanel.tsx (2)

67-67: Consider marking exported component props as read-only.

Similar to the nested component, the exported AgentStatusPanel props can be wrapped with Readonly<> for consistency.

♻️ Proposed fix
-export function AgentStatusPanel({ compact = false }: { compact?: boolean }) {
+export function AgentStatusPanel({ compact = false }: Readonly<{ compact?: boolean }>) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-events/AgentStatusPanel.tsx` at line 67, Wrap the
exported AgentStatusPanel props type in Readonly to match the nested component's
immutability pattern: update the component signature for AgentStatusPanel (the
function and its props annotation) to use Readonly<{ compact?: boolean }> so the
incoming props are treated as read-only; ensure any other local references to
the props still work with the readonly type.

44-65: Consider marking props as read-only for type safety.

SonarCloud flags the props types. Using Readonly<> wrapper or readonly modifier improves immutability guarantees.

♻️ Proposed fix
-function AgentStatusBadge({ entry, compact }: { entry: AgentStatusEntry; compact?: boolean }) {
+function AgentStatusBadge({ entry, compact }: Readonly<{ entry: AgentStatusEntry; compact?: boolean }>) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-events/AgentStatusPanel.tsx` around lines 44 - 65,
Change the AgentStatusBadge props typing to be read-only to satisfy SonarCloud:
update the function parameter type from { entry: AgentStatusEntry; compact?:
boolean } to a readonly variant (e.g. Readonly<{ entry: AgentStatusEntry;
compact?: boolean }> or make entry: Readonly<AgentStatusEntry>) so the props
(entry and compact) cannot be mutated inside AgentStatusBadge; keep the same
destructuring ({ entry, compact }) and UI logic but ensure the type reflects
immutability.
web/src/components/agent-dashboard/KanbanBoard.tsx (1)

69-71: Optional: pre-group tasks once to avoid repeated filtering per column.

Current logic is correct, but tasks.filter(...) runs once per column every render. For larger boards, grouping by status once and reusing buckets will reduce repeated scans.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/agent-dashboard/KanbanBoard.tsx` around lines 69 - 71, The
current mapping in KanbanBoard calls tasks.filter(...) inside COLUMNS.map,
causing repeated scans; instead, pre-group tasks once (e.g., build a Map or
object keyed by status or column id) before rendering, then inside the
COLUMNS.map use that lookup to assemble colTasks for each column based on
col.statuses; update references to colTasks and keep the rest of the rendering
in the KanbanBoard component unchanged.
tests/unit/test_agent_events_vocab.py (1)

163-187: Avoid brittle source-text assertions for _audit.py.

test_audit_uses_constants can pass/fail due to formatting or comment text rather than semantics. Prefer AST-based assertions (or behavior-level assertions) so the test validates actual code intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test_agent_events_vocab.py` around lines 163 - 187, The current
test_audit_uses_constants is brittle because it inspects raw source text; update
it to parse _audit.py into an AST and assert semantics: use ast.parse on the
file contents, walk Call nodes and collect keyword args named "type", then
assert at least one such keyword has a value that is an Attribute or Name
referencing EventType.TOOL_ERROR and at least one referencing
EventType.TOOL_RESULT, and assert there are no keyword "type" values that are
Constant strings "error" or "tool_result"; update references in the test to use
the function name test_audit_uses_constants and the module _audit.py and check
for Attribute(Name("EventType"), "TOOL_ERROR"/"TOOL_RESULT") rather than raw
string containment.

Comment on lines +1 to +63
---
phase: 09-three-panel-dashboard
plan: "01"
type: execute
wave: 1
depends_on: []
files_modified:
- src/paperbot/application/collaboration/message_schema.py
- tests/unit/test_agent_events_vocab.py
- web/src/lib/agent-events/types.ts
- web/src/lib/agent-events/parsers.ts
- web/src/lib/agent-events/parsers.test.ts
- web/src/lib/agent-events/store.ts
- web/src/lib/agent-events/store.test.ts
- web/src/lib/agent-events/useAgentEvents.ts
autonomous: true
requirements: [DASH-01, FILE-01, FILE-02]

must_haves:
truths:
- "FILE_CHANGE event type exists in Python EventType constants"
- "FileTouchedEntry type is exported from types.ts"
- "parseFileTouched() extracts file entries from file_change and write_file tool_result events"
- "parseFileTouched() returns null for non-file events"
- "Store tracks filesTouched keyed by run_id with dedup and 20-run eviction"
- "SSE hook dispatches file touch events to store alongside existing parsers"
artifacts:
- path: "src/paperbot/application/collaboration/message_schema.py"
provides: "EventType.FILE_CHANGE constant"
contains: "FILE_CHANGE"
- path: "web/src/lib/agent-events/types.ts"
provides: "FileTouchedEntry and FileChangeStatus types"
exports: ["FileTouchedEntry", "FileChangeStatus"]
- path: "web/src/lib/agent-events/parsers.ts"
provides: "parseFileTouched function"
exports: ["parseFileTouched"]
- path: "web/src/lib/agent-events/store.ts"
provides: "filesTouched, selectedRunId, selectedFile state + actions"
exports: ["useAgentEventStore"]
- path: "web/src/lib/agent-events/useAgentEvents.ts"
provides: "SSE hook dispatching parseFileTouched results"
key_links:
- from: "web/src/lib/agent-events/parsers.ts"
to: "web/src/lib/agent-events/types.ts"
via: "import FileTouchedEntry"
pattern: "import.*FileTouchedEntry.*from.*types"
- from: "web/src/lib/agent-events/useAgentEvents.ts"
to: "web/src/lib/agent-events/parsers.ts"
via: "import parseFileTouched"
pattern: "import.*parseFileTouched.*from.*parsers"
- from: "web/src/lib/agent-events/useAgentEvents.ts"
to: "web/src/lib/agent-events/store.ts"
via: "calls addFileTouched"
pattern: "addFileTouched"
---

<objective>
Extend the agent event data layer with file-change tracking: Python EventType.FILE_CHANGE constant, TypeScript FileTouchedEntry type, parseFileTouched() parser, Zustand store extension (filesTouched + selectedRunId + selectedFile), and SSE hook wiring.

Purpose: Plan 02 (UI components) needs these types and store fields to render the file list panel and inline diff panel. This plan defines the data contracts first.

Output: Extended store.ts, parsers.ts, types.ts with file tracking; message_schema.py with FILE_CHANGE; all with passing tests.
</objective>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This file appears out of scope for the stated PR objective.

The PR objective is SonarCloud CI setup, but this file introduces Phase 9 product-work execution planning. Please move unrelated planning changes to a separate PR to keep review scope and release risk controlled.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/09-three-panel-dashboard/09-01-PLAN.md around lines 1 - 63,
The .planning/phases/09-three-panel-dashboard/09-01-PLAN.md changes are out of
scope for this PR (SonarCloud CI setup); revert or remove this file change from
the current branch and move it into a separate PR/branch dedicated to product
execution planning (the block containing "phase: 09-three-panel-dashboard" and
the "must_haves"/"artifacts" content should be extracted). Concretely, undo the
commit or create a new commit that reverts edits to 09-01-PLAN.md in this
branch, then open a new PR with the planning additions so only CI-related files
remain in this PR; ensure tests/CI still pass after revert.


cd "$(dirname "$0")/../.."

export PYTHONPATH="${PYTHONPATH:-src}"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Always include src in PYTHONPATH.

Line 6 preserves any pre-existing PYTHONPATH as-is; if it does not include src, imports can fail unexpectedly in CI/local runners.

💡 Proposed fix
-export PYTHONPATH="${PYTHONPATH:-src}"
+export PYTHONPATH="src${PYTHONPATH:+:${PYTHONPATH}}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/ci/run_backend_offline_gates.sh` at line 6, The current export
preserves PYTHONPATH as-is which can omit src; change the export line that sets
PYTHONPATH so that it always prepends "src" to the existing PYTHONPATH, using
shell parameter expansion to add the colon only when PYTHONPATH is non-empty
(i.e., replace the existing export of PYTHONPATH with one that guarantees src is
first in PYTHONPATH while preserving any existing entries).

Comment on lines +828 to +833
await _emit_codex_event(
"codex_dispatched",
task,
session,
{"assignee": task.assignee},
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wire codex lifecycle events through the actual execution paths.

These are the only _emit_codex_event() call sites in the module. The main /sessions/{session_id}/run flow and _execute_task_stream_sandbox() never invoke the helper, so the dashboard will miss codex_* events for the default run path and sandbox-as-workspace execution.

Also applies to: 2430-2545

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/paperbot/api/routes/agent_board.py` around lines 828 - 833, The dashboard
is missing codex lifecycle events because _emit_codex_event is only called in a
few spots; add calls into the main run flow handler for the
/sessions/{session_id}/run path and inside _execute_task_stream_sandbox at the
same lifecycle points you already emit events elsewhere (e.g., when a task is
dispatched, started, completed, failed); call _emit_codex_event with the same
signature used at the existing site (event name, task, session, payload like
{"assignee": task.assignee}) so the dashboard sees
codex_dispatched/codex_started/codex_completed/codex_failed for both default run
and sandbox-as-workspace execution.

Comment on lines +17 to +56
const [viewMode, setViewMode] = useState<"panels" | "kanban">("panels")

const studioTasks = useStudioStore((s) => s.agentTasks)
const eventKanbanTasks = useAgentEventStore((s) => s.kanbanTasks)
const kanbanTasks = studioTasks.length > 0 ? studioTasks : eventKanbanTasks

return (
<div className="h-screen min-h-0 flex flex-col">
<header className="border-b px-4 py-2 flex items-center gap-3 shrink-0">
<h1 className="text-sm font-semibold">Agent Dashboard</h1>
<div className="flex items-center gap-1 ml-auto">
<button
onClick={() => setViewMode("panels")}
className={`p-1.5 rounded ${viewMode === "panels" ? "bg-muted" : "hover:bg-muted/50"}`}
title="Panels view"
aria-label="Switch to panels view"
>
<Columns3 className="h-4 w-4" />
</button>
<button
onClick={() => setViewMode("kanban")}
className={`p-1.5 rounded ${viewMode === "kanban" ? "bg-muted" : "hover:bg-muted/50"}`}
title="Kanban view"
aria-label="Switch to kanban view"
>
<LayoutGrid className="h-4 w-4" />
</button>
</div>
</header>
<div className="flex-1 min-h-0">
{viewMode === "panels" ? (
<SplitPanels
storageKey="agent-dashboard"
rail={<TasksPanel />}
list={<ActivityFeed />}
detail={<FileListPanel />}
/>
) : (
<KanbanBoard tasks={kanbanTasks} />
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wire the DAG panel into the dashboard toggle.

AgentDagPanel is added in this change set, but this page still only allows "panels" and "kanban" and never renders it. As shipped, the DAG feature is unreachable from /agent-dashboard.

Minimal wiring example
-import { Columns3, LayoutGrid } from "lucide-react"
+import { Columns3, LayoutGrid, Workflow } from "lucide-react"
 ...
 import { KanbanBoard } from "@/components/agent-dashboard/KanbanBoard"
+import { AgentDagPanel } from "@/components/agent-dashboard/AgentDagPanel"
 ...
-  const [viewMode, setViewMode] = useState<"panels" | "kanban">("panels")
+  const [viewMode, setViewMode] = useState<"panels" | "kanban" | "dag">("panels")
 ...
           <button
             onClick={() => setViewMode("kanban")}
             className={`p-1.5 rounded ${viewMode === "kanban" ? "bg-muted" : "hover:bg-muted/50"}`}
             title="Kanban view"
             aria-label="Switch to kanban view"
           >
             <LayoutGrid className="h-4 w-4" />
           </button>
+          <button
+            onClick={() => setViewMode("dag")}
+            className={`p-1.5 rounded ${viewMode === "dag" ? "bg-muted" : "hover:bg-muted/50"}`}
+            title="DAG view"
+            aria-label="Switch to DAG view"
+          >
+            <Workflow className="h-4 w-4" />
+          </button>
         </div>
       </header>
       <div className="flex-1 min-h-0">
         {viewMode === "panels" ? (
           <SplitPanels
             storageKey="agent-dashboard"
             rail={<TasksPanel />}
             list={<ActivityFeed />}
             detail={<FileListPanel />}
           />
-        ) : (
+        ) : viewMode === "kanban" ? (
           <KanbanBoard tasks={kanbanTasks} />
+        ) : (
+          <AgentDagPanel />
         )}
       </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/agent-dashboard/page.tsx` around lines 17 - 56, The dashboard
never exposes the new DAG panel because viewMode only supports "panels" and
"kanban"; update the viewMode state type/default and toggle buttons to include a
third mode (e.g., "dag") and render AgentDagPanel when that mode is selected.
Specifically, change useState<"panels" | "kanban">("panels") to include "dag",
add a third button that calls setViewMode("dag") (matching the existing button
styling and aria/title patterns), and update the JSX conditional where
SplitPanels and KanbanBoard are chosen to render AgentDagPanel (or include it
inside SplitPanels) when viewMode === "dag"; reference viewMode, setViewMode,
SplitPanels, KanbanBoard, and AgentDagPanel to locate the spots to modify.

Comment on lines +78 to +83
it("handles depends_on referencing non-existent task ids (defaults dependency to 0)", () => {
const tasks = [makeTask("t1", "Task 1", "planning", ["missing-id"])]
const depths = computeTaskDepths(tasks)
// missing-id not in task list → treated as 0 depth, so t1 = max(0)+1 = 1
expect(depths.get("t1")).toBe(1)
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unknown dependency ids shouldn't create extra depth.

The backend DAG ignores unknown dependencies rather than pushing the task deeper. Expecting depth 1 here will make the frontend layout disagree with the execution DAG whenever the LLM emits a bad depends_on reference.

Based on learnings, src/paperbot/infrastructure/swarm/task_dag.py intentionally ignores unknown dependencies and schedules cyclic nodes as a final batch instead of failing closed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/dag.test.ts` around lines 78 - 83, The test for
computeTaskDepths incorrectly expects unknown dependency ids to increase depth;
update the test case (the "handles depends_on referencing non-existent task
ids..." spec that uses makeTask("t1", ...) and calls computeTaskDepths) so that
unknown dependencies are ignored and t1's depth is 0 (change
expect(depths.get("t1")).toBe(1) to expect(...).toBe(0)), and adjust the inline
comment to state that missing-id is ignored by the DAG rather than treated as
depth 0+1.

Comment on lines +25 to +62
export function computeTaskDepths(tasks: AgentTask[]): Map<string, number> {
const idSet = new Set(tasks.map((t) => t.id))
const depthMap = new Map<string, number>()

// Iterative depth computation: keep resolving until stable
// Initialize all to 0
for (const task of tasks) {
depthMap.set(task.id, 0)
}

// Topological resolution with a cap to handle circular references
const maxIterations = tasks.length + 1
for (let iter = 0; iter < maxIterations; iter++) {
let changed = false
for (const task of tasks) {
const deps = task.depends_on ?? []
if (deps.length === 0) continue
let maxDepDepth = -1
for (const depId of deps) {
if (idSet.has(depId)) {
const depDepth = depthMap.get(depId) ?? 0
if (depDepth > maxDepDepth) maxDepDepth = depDepth
} else {
// Missing dependency treated as depth 0
if (maxDepDepth < 0) maxDepDepth = 0
}
}
const newDepth = maxDepDepth + 1
if ((depthMap.get(task.id) ?? 0) !== newDepth) {
depthMap.set(task.id, newDepth)
changed = true
}
}
if (!changed) break
}

return depthMap
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Cycle handling in depth computation is unstable and inflates depths.

Line 37–58 updates depthMap in place. For cyclic graphs, depths keep increasing each iteration (until the cap), producing order-dependent and exaggerated x-positions. Use a deterministic fail-open topo pass, then place unresolved cyclic nodes in one final depth bucket.

💡 Proposed refactor for deterministic fail-open depth assignment
 export function computeTaskDepths(tasks: AgentTask[]): Map<string, number> {
-  const idSet = new Set(tasks.map((t) => t.id))
-  const depthMap = new Map<string, number>()
-
-  // Iterative depth computation: keep resolving until stable
-  // Initialize all to 0
-  for (const task of tasks) {
-    depthMap.set(task.id, 0)
-  }
-
-  // Topological resolution with a cap to handle circular references
-  const maxIterations = tasks.length + 1
-  for (let iter = 0; iter < maxIterations; iter++) {
-    let changed = false
-    for (const task of tasks) {
-      const deps = task.depends_on ?? []
-      if (deps.length === 0) continue
-      let maxDepDepth = -1
-      for (const depId of deps) {
-        if (idSet.has(depId)) {
-          const depDepth = depthMap.get(depId) ?? 0
-          if (depDepth > maxDepDepth) maxDepDepth = depDepth
-        } else {
-          // Missing dependency treated as depth 0
-          if (maxDepDepth < 0) maxDepDepth = 0
-        }
-      }
-      const newDepth = maxDepDepth + 1
-      if ((depthMap.get(task.id) ?? 0) !== newDepth) {
-        depthMap.set(task.id, newDepth)
-        changed = true
-      }
-    }
-    if (!changed) break
-  }
-
-  return depthMap
+  const idSet = new Set(tasks.map((t) => t.id))
+  const depthMap = new Map<string, number>(tasks.map((t) => [t.id, 0]))
+  const indegree = new Map<string, number>(tasks.map((t) => [t.id, 0]))
+  const dependents = new Map<string, string[]>()
+
+  for (const task of tasks) {
+    for (const depId of task.depends_on ?? []) {
+      if (!idSet.has(depId)) continue
+      indegree.set(task.id, (indegree.get(task.id) ?? 0) + 1)
+      if (!dependents.has(depId)) dependents.set(depId, [])
+      dependents.get(depId)!.push(task.id)
+    }
+  }
+
+  const queue: string[] = []
+  for (const task of tasks) {
+    if ((indegree.get(task.id) ?? 0) === 0) queue.push(task.id)
+  }
+
+  while (queue.length > 0) {
+    const current = queue.shift()!
+    const currentDepth = depthMap.get(current) ?? 0
+    for (const next of dependents.get(current) ?? []) {
+      depthMap.set(next, Math.max(depthMap.get(next) ?? 0, currentDepth + 1))
+      indegree.set(next, (indegree.get(next) ?? 0) - 1)
+      if ((indegree.get(next) ?? 0) === 0) queue.push(next)
+    }
+  }
+
+  const resolvedMax = Array.from(depthMap.values()).reduce((m, d) => Math.max(m, d), 0)
+  for (const task of tasks) {
+    if ((indegree.get(task.id) ?? 0) > 0) {
+      depthMap.set(task.id, resolvedMax + 1)
+    }
+  }
+
+  return depthMap
 }

Based on learnings: In src/paperbot/infrastructure/swarm/task_dag.py, the TaskDAG class intentionally uses fault-tolerant/fail-open behavior: unknown dependencies are silently ignored and cyclic nodes are scheduled as a final batch instead of raising errors.

📝 Committable suggestion

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

Suggested change
export function computeTaskDepths(tasks: AgentTask[]): Map<string, number> {
const idSet = new Set(tasks.map((t) => t.id))
const depthMap = new Map<string, number>()
// Iterative depth computation: keep resolving until stable
// Initialize all to 0
for (const task of tasks) {
depthMap.set(task.id, 0)
}
// Topological resolution with a cap to handle circular references
const maxIterations = tasks.length + 1
for (let iter = 0; iter < maxIterations; iter++) {
let changed = false
for (const task of tasks) {
const deps = task.depends_on ?? []
if (deps.length === 0) continue
let maxDepDepth = -1
for (const depId of deps) {
if (idSet.has(depId)) {
const depDepth = depthMap.get(depId) ?? 0
if (depDepth > maxDepDepth) maxDepDepth = depDepth
} else {
// Missing dependency treated as depth 0
if (maxDepDepth < 0) maxDepDepth = 0
}
}
const newDepth = maxDepDepth + 1
if ((depthMap.get(task.id) ?? 0) !== newDepth) {
depthMap.set(task.id, newDepth)
changed = true
}
}
if (!changed) break
}
return depthMap
}
export function computeTaskDepths(tasks: AgentTask[]): Map<string, number> {
const idSet = new Set(tasks.map((t) => t.id))
const depthMap = new Map<string, number>(tasks.map((t) => [t.id, 0]))
const indegree = new Map<string, number>(tasks.map((t) => [t.id, 0]))
const dependents = new Map<string, string[]>()
for (const task of tasks) {
for (const depId of task.depends_on ?? []) {
if (!idSet.has(depId)) continue
indegree.set(task.id, (indegree.get(task.id) ?? 0) + 1)
if (!dependents.has(depId)) dependents.set(depId, [])
dependents.get(depId)!.push(task.id)
}
}
const queue: string[] = []
for (const task of tasks) {
if ((indegree.get(task.id) ?? 0) === 0) queue.push(task.id)
}
while (queue.length > 0) {
const current = queue.shift()!
const currentDepth = depthMap.get(current) ?? 0
for (const next of dependents.get(current) ?? []) {
depthMap.set(next, Math.max(depthMap.get(next) ?? 0, currentDepth + 1))
indegree.set(next, (indegree.get(next) ?? 0) - 1)
if ((indegree.get(next) ?? 0) === 0) queue.push(next)
}
}
const resolvedMax = Array.from(depthMap.values()).reduce((m, d) => Math.max(m, d), 0)
for (const task of tasks) {
if ((indegree.get(task.id) ?? 0) > 0) {
depthMap.set(task.id, resolvedMax + 1)
}
}
return depthMap
}
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[failure] 25-25: Refactor this function to reduce its Cognitive Complexity from 30 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=jerry609_PaperBot&issues=AZzv6RhXwAnQ9xtGYWX-&open=AZzv6RhXwAnQ9xtGYWX-&pullRequest=397

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/dag.ts` around lines 25 - 62, computeTaskDepths
currently updates depthMap in place across iterations causing cyclic
dependencies to inflate depths; replace the iterative in-place update with a
deterministic fail-open topo pass: use idSet and depends_on to perform a
Kahn-like resolution where you repeatedly dequeue tasks with all resolved deps
(treat missing deps as resolved/ignored), compute their depth from resolved deps
into a new resolvedDepths map (use stable ordering like sorting by id when
enqueuing to be deterministic), and collect any leftover nodes after the pass as
the cyclic/unresolved set; assign all leftover cyclic nodes a single final depth
of (maxResolvedDepth + 1) so cycles are placed together rather than growing each
iteration, and return the resolvedDepths map (including assigned cycle depths)
instead of mutating depthMap in-place or iterating maxIterations.

Comment on lines +114 to +124
for (const entry of scoreEdges) {
edges.push({
id: `score-${entry.id}`,
source: entry.from_agent,
target: entry.to_agent,
type: "scoreFlow",
label: `${entry.stage}: ${entry.score.toFixed(2)}`,
style: { strokeDasharray: "5 3" },
animated: false,
})
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Score edges are added without ensuring endpoints exist in the task-node graph.

buildDagNodes() creates nodes keyed by task.id, but Line 117–118 uses from_agent/to_agent for score edges. If those IDs are not present in idSet, the returned edge list contains disconnected edges.

🔧 Proposed guard
   // ScoreShareBus edges
   for (const entry of scoreEdges) {
+    if (!idSet.has(entry.from_agent) || !idSet.has(entry.to_agent)) {
+      continue
+    }
     edges.push({
       id: `score-${entry.id}`,
       source: entry.from_agent,
       target: entry.to_agent,
       type: "scoreFlow",
📝 Committable suggestion

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

Suggested change
for (const entry of scoreEdges) {
edges.push({
id: `score-${entry.id}`,
source: entry.from_agent,
target: entry.to_agent,
type: "scoreFlow",
label: `${entry.stage}: ${entry.score.toFixed(2)}`,
style: { strokeDasharray: "5 3" },
animated: false,
})
}
for (const entry of scoreEdges) {
if (!idSet.has(entry.from_agent) || !idSet.has(entry.to_agent)) {
continue
}
edges.push({
id: `score-${entry.id}`,
source: entry.from_agent,
target: entry.to_agent,
type: "scoreFlow",
label: `${entry.stage}: ${entry.score.toFixed(2)}`,
style: { strokeDasharray: "5 3" },
animated: false,
})
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/dag.ts` around lines 114 - 124, buildDagNodes()
constructs nodes keyed by task.id but the loop that pushes score edges
(iterating scoreEdges and pushing into edges with id `score-${entry.id}` and
source/target from entry.from_agent/entry.to_agent) doesn’t verify those
endpoints exist; update the loop to guard each edge by checking the node id set
(e.g., idSet.has(entry.from_agent) and idSet.has(entry.to_agent)) and only push
the score edge if both endpoints are present (optionally log or skip otherwise)
so you don’t return disconnected edges.

Comment on lines +128 to +141
const path = String(
(isExplicitFileChange
? payload.path
: payload.arguments
? (payload.arguments as Record<string, unknown>).path
: undefined) ?? ""
)
if (!path) return null

return {
run_id: String(raw.run_id),
path,
status: (payload.status as "created" | "modified") ?? "modified",
ts: String(raw.ts),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate path and status before emitting FileTouchedEntry.

String(...) coercion here can accept non-string paths (e.g., objects become "[object Object]"), and status is unchecked via type assertion. That can pollute filesTouched with invalid entries.

Suggested fix
-  const path = String(
-    (isExplicitFileChange
-      ? payload.path
-      : payload.arguments
-        ? (payload.arguments as Record<string, unknown>).path
-        : undefined) ?? ""
-  )
-  if (!path) return null
+  const rawPath = isExplicitFileChange
+    ? payload.path
+    : typeof payload.arguments === "object" && payload.arguments !== null
+      ? (payload.arguments as Record<string, unknown>).path
+      : undefined
+  if (typeof rawPath !== "string" || rawPath.trim() === "") return null
+  const path = rawPath
+
+  const status: FileTouchedEntry["status"] =
+    payload.status === "created" || payload.status === "modified"
+      ? payload.status
+      : "modified"
@@
-    status: (payload.status as "created" | "modified") ?? "modified",
+    status,
As per coding guidelines "`{src,web/src}/**/*.{py,ts,tsx}`: If behavior changes, add or update tests".
📝 Committable suggestion

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

Suggested change
const path = String(
(isExplicitFileChange
? payload.path
: payload.arguments
? (payload.arguments as Record<string, unknown>).path
: undefined) ?? ""
)
if (!path) return null
return {
run_id: String(raw.run_id),
path,
status: (payload.status as "created" | "modified") ?? "modified",
ts: String(raw.ts),
const rawPath = isExplicitFileChange
? payload.path
: typeof payload.arguments === "object" && payload.arguments !== null
? (payload.arguments as Record<string, unknown>).path
: undefined
if (typeof rawPath !== "string" || rawPath.trim() === "") return null
const path = rawPath
const status: FileTouchedEntry["status"] =
payload.status === "created" || payload.status === "modified"
? payload.status
: "modified"
return {
run_id: String(raw.run_id),
path,
status,
ts: String(raw.ts),
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 129-133: '(isExplicitFileChange
? payload.path
: payload.arguments
? (payload.arguments as Record<string, unknown>).path
: undefined) ?? ""' will use Object's default stringification format ('[object Object]') when stringified.

See more on https://sonarcloud.io/project/issues?id=jerry609_PaperBot&issues=AZzv6RhLwAnQ9xtGYWX2&open=AZzv6RhLwAnQ9xtGYWX2&pullRequest=397


[warning] 131-133: Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=jerry609_PaperBot&issues=AZzv6RhLwAnQ9xtGYWX3&open=AZzv6RhLwAnQ9xtGYWX3&pullRequest=397

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/parsers.ts` around lines 128 - 141, The parser
currently coerces payload paths and force-casts status which allows invalid
entries (e.g. "[object Object]") into FileTouchedEntry; update the logic around
the computed path (the path variable derived from isExplicitFileChange,
payload.path, and payload.arguments) to explicitly validate that it is a
non-empty string (reject values that are objects/arrays or stringified objects)
and return null if it fails, and validate payload.status so only "created" or
"modified" are accepted (otherwise default to "modified" or return null per the
module convention); adjust the code paths that produce FileTouchedEntry (run_id,
path, status, ts) accordingly and add/update unit tests for parsers handling
invalid path types and bad status values.

Comment on lines +77 to +91
addFileTouched: (entry) =>
set((s) => {
const existing = s.filesTouched[entry.run_id] ?? []
// Dedup by path: ignore if same path already tracked for this run
if (existing.some((e) => e.path === entry.path)) return s
const updated = {
...s.filesTouched,
[entry.run_id]: [...existing, entry],
}
// Evict oldest run_id when exceeding 20 runs
const keys = Object.keys(updated)
if (keys.length > 20) {
delete updated[keys[0]]
}
return { filesTouched: updated }
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Upsert repeated file touches instead of dropping them.

A single file can be written several times during one run. Returning early on the second event keeps stale status, ts, and line counts in the store, so the file panel can show outdated data for any path touched more than once.

Suggested change
   addFileTouched: (entry) =>
     set((s) => {
       const existing = s.filesTouched[entry.run_id] ?? []
-      // Dedup by path: ignore if same path already tracked for this run
-      if (existing.some((e) => e.path === entry.path)) return s
+      const nextForRun = existing.some((e) => e.path === entry.path)
+        ? existing.map((e) => (e.path === entry.path ? entry : e))
+        : [...existing, entry]
       const updated = {
         ...s.filesTouched,
-        [entry.run_id]: [...existing, entry],
+        [entry.run_id]: nextForRun,
       }
       // Evict oldest run_id when exceeding 20 runs
       const keys = Object.keys(updated)
📝 Committable suggestion

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

Suggested change
addFileTouched: (entry) =>
set((s) => {
const existing = s.filesTouched[entry.run_id] ?? []
// Dedup by path: ignore if same path already tracked for this run
if (existing.some((e) => e.path === entry.path)) return s
const updated = {
...s.filesTouched,
[entry.run_id]: [...existing, entry],
}
// Evict oldest run_id when exceeding 20 runs
const keys = Object.keys(updated)
if (keys.length > 20) {
delete updated[keys[0]]
}
return { filesTouched: updated }
addFileTouched: (entry) =>
set((s) => {
const existing = s.filesTouched[entry.run_id] ?? []
const nextForRun = existing.some((e) => e.path === entry.path)
? existing.map((e) => (e.path === entry.path ? entry : e))
: [...existing, entry]
const updated = {
...s.filesTouched,
[entry.run_id]: nextForRun,
}
// Evict oldest run_id when exceeding 20 runs
const keys = Object.keys(updated)
if (keys.length > 20) {
delete updated[keys[0]]
}
return { filesTouched: updated }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/store.ts` around lines 77 - 91, The addFileTouched
updater currently returns early if a file path already exists for a run, leaving
stale status/ts/line counts; change it to upsert the entry instead: inside
addFileTouched (where existing is computed from s.filesTouched[entry.run_id])
find if an entry with the same path exists and replace that element with the new
entry (or merge updated fields) otherwise append the new entry, then continue to
build updated and perform the same eviction of oldest run ids; this ensures
repeated touches update the stored record rather than being ignored.

Comment on lines +21 to +57
const res = await fetch(`${BACKEND_URL}/api/events/stream`, {
signal: controller.signal,
headers: { Accept: "text/event-stream" },
})
if (!res.ok || !res.body) return
setConnected(true)

for await (const msg of readSSE(res.body)) {
const raw = msg as unknown as AgentEventEnvelopeRaw
if (!raw?.type) continue

const feedItem = parseActivityItem(raw)
if (feedItem) addFeedItem(feedItem)

const statusEntry = parseAgentStatus(raw)
if (statusEntry) updateAgentStatus(statusEntry)

const toolCall = parseToolCall(raw)
if (toolCall) addToolCall(toolCall)

const fileTouched = parseFileTouched(raw)
if (fileTouched) addFileTouched(fileTouched)

const codexDel = parseCodexDelegation(raw)
if (codexDel) addCodexDelegation(codexDel)

const scoreEdge = parseScoreEdge(raw)
if (scoreEdge) addScoreEdge(scoreEdge)
}
} catch (err) {
if ((err as Error)?.name !== "AbortError") {
console.warn("[useAgentEvents] disconnected, will retry in 3s", err)
setTimeout(connect, 3000)
}
} finally {
setConnected(false)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reconnect is skipped on non-OK responses and clean stream termination.

At Line [25], the function returns immediately for !res.ok || !res.body and never schedules retry. Also, when the for await loop (Line [28]) ends without throwing, the hook sets disconnected in finally (Line [56]) but does not reconnect. This can leave the UI disconnected until manual reload.

🔧 Suggested fix
 export function useAgentEvents() {
   const { setConnected, addFeedItem, updateAgentStatus, addToolCall, addFileTouched, addCodexDelegation, addScoreEdge } = useAgentEventStore()
   const abortRef = useRef<AbortController | null>(null)
+  const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)

   useEffect(() => {
     const controller = new AbortController()
     abortRef.current = controller

     async function connect() {
+      let shouldRetry = false
       try {
         const res = await fetch(`${BACKEND_URL}/api/events/stream`, {
           signal: controller.signal,
           headers: { Accept: "text/event-stream" },
         })
-        if (!res.ok || !res.body) return
+        if (!res.ok || !res.body) {
+          shouldRetry = true
+          return
+        }
         setConnected(true)

         for await (const msg of readSSE(res.body)) {
           const raw = msg as unknown as AgentEventEnvelopeRaw
           if (!raw?.type) continue
@@
           const scoreEdge = parseScoreEdge(raw)
           if (scoreEdge) addScoreEdge(scoreEdge)
         }
+        if (!controller.signal.aborted) shouldRetry = true
       } catch (err) {
         if ((err as Error)?.name !== "AbortError") {
           console.warn("[useAgentEvents] disconnected, will retry in 3s", err)
-          setTimeout(connect, 3000)
+          shouldRetry = true
         }
       } finally {
         setConnected(false)
+        if (shouldRetry && !controller.signal.aborted) {
+          retryTimerRef.current = setTimeout(connect, 3000)
+        }
       }
     }

     connect()
     return () => {
+      if (retryTimerRef.current) clearTimeout(retryTimerRef.current)
       controller.abort()
     }
   }, [setConnected, addFeedItem, updateAgentStatus, addToolCall, addFileTouched, addCodexDelegation, addScoreEdge])
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/lib/agent-events/useAgentEvents.ts` around lines 21 - 57, When the
SSE connect logic in useAgentEvents (inside connect) returns early for !res.ok
|| !res.body it currently never retries; change that branch to log the failure
and schedule a reconnect via setTimeout(connect, 3000) instead of returning
immediately, and ensure the finally block also triggers a retry when the stream
ends cleanly (i.e., when no error or when error is AbortError do not retry) by
checking the controller.signal.aborted or the caught error and calling
setTimeout(connect, 3000) when appropriate; refer to the connect function, the
readSSE loop, setConnected, and the existing setTimeout(connect, 3000) usage to
implement consistent retry behavior on non-OK responses and normal stream
termination.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a SonarCloud CI workflow for code quality scanning, along with the necessary coverage reporting infrastructure for both the Python backend and the Next.js frontend. It also refactors the backend test gate from an inline CI step into a reusable shell script.

Changes:

  • Added .github/workflows/sonarcloud.yml workflow that runs backend and frontend tests with coverage, then submits results to SonarCloud
  • Extracted the inline backend offline test list from ci.yml into a shared scripts/ci/run_backend_offline_gates.sh script, reused by both CI and SonarCloud workflows
  • Configured coverage tooling: pytest-cov for Python (coverage.xml), @vitest/coverage-v8 for the web frontend (lcov.info), and sonar-project.properties for SonarCloud ingestion

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/sonarcloud.yml New workflow: runs backend + frontend tests with coverage, then invokes SonarCloud scan
.github/workflows/ci.yml Replaced inline test list with call to shared run_backend_offline_gates.sh
scripts/ci/run_backend_offline_gates.sh New shared script listing all offline test files, forwarding extra args ($@) to pytest
sonar-project.properties SonarCloud project configuration (sources, tests, coverage paths)
pyproject.toml Added pytest-cov dev dependency and [tool.coverage.*] sections
requirements-ci.txt Added pytest-cov for CI
web/vitest.config.ts Added v8 coverage config and jsdom environment globs
web/package.json Added test:coverage script and @vitest/coverage-v8 dev dependency
web/package-lock.json Lock file updates for new coverage dependencies
.gitignore Ignore coverage artifacts and .scannerwork/
Files not reviewed (1)
  • web/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.


sonar.python.coverage.reportPaths=coverage.xml
sonar.javascript.lcov.reportPaths=web/coverage/lcov.info
sonar.typescript.lcov.reportPaths=web/coverage/lcov.info
@sonarqubecloud
Copy link

@jerry609 jerry609 merged commit 54239e8 into dev Mar 15, 2026
19 checks passed
@jerry609 jerry609 deleted the feat/sonarcloud-ci branch March 15, 2026 06:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds SonarCloud CI integration with coverage reporting for both Python backend and TypeScript/React frontend. It extracts a shared backend test gate script to avoid duplicating the test file list between the main CI and SonarCloud workflows, configures coverage tooling (pytest-cov for Python, @vitest/coverage-v8 for frontend), and adds the SonarCloud project configuration.

Changes:

  • New SonarCloud GitHub Actions workflow with graceful handling of missing secrets and fork PRs
  • Extracted backend offline test gates into a reusable shell script (scripts/ci/run_backend_offline_gates.sh), replacing inline test lists in ci.yml
  • Added coverage configuration for both Python (pytest-cov, coverage.xml) and frontend (@vitest/coverage-v8, lcov.info) along with sonar-project.properties

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
.github/workflows/sonarcloud.yml New workflow for SonarCloud scan with coverage collection
.github/workflows/ci.yml Replaced inline pytest invocation with shared script
scripts/ci/run_backend_offline_gates.sh Extracted reusable offline test gate script
sonar-project.properties SonarCloud project configuration with source/test/coverage paths
pyproject.toml Added pytest-cov dependency and [tool.coverage] config
requirements-ci.txt Added pytest-cov to CI dependencies
web/vitest.config.ts Added coverage config (v8 provider, lcov reporter) and jsdom environment for component tests
web/package.json Added test:coverage script and @vitest/coverage-v8 dev dependency
web/package-lock.json Lock file updates for new dependencies
.gitignore Ignore coverage artifacts and SonarCloud scanner work directory
Files not reviewed (1)
  • web/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +30 to +31
SONAR_ORGANIZATION: ${{ vars.SONAR_ORGANIZATION || 'jerry609' }}
SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY || 'jerry609_PaperBot' }}
Comment on lines +109 to +117
- name: SonarCloud scan
if: steps.sonar_config.outputs.scan_enabled == 'true'
uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7.0.0
with:
args: >
-Dsonar.organization=${{ env.SONAR_ORGANIZATION }}
-Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
-Dsonar.qualitygate.wait=true
-Dsonar.qualitygate.timeout=300
Comment on lines +105 to +107
key: ${{ runner.os }}-sonar-cache
restore-keys: |
${{ runner.os }}-sonar-cache
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants