Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThis 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
Vercel Preview
|
There was a problem hiding this comment.
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.
| await _emit_codex_event( | ||
| "codex_dispatched", | ||
| task, | ||
| session, | ||
| {"assignee": task.assignee}, | ||
| ) |
| 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}" |
3243877 to
4d4479f
Compare
Summary of ChangesHello, 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
🧠 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
Ignored Files
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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)
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)
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)
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.
There was a problem hiding this comment.
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 | 🟠 MajorAvoid double-dispatch and premature acceptance states.
If a client calls
/tasks/{task_id}/dispatchand then/tasks/{task_id}/execute, this branch emits a secondcodex_dispatchedfor the same task. It also appendscodex_acceptedbeforebuild_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 | 🟡 MinorAlign 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 processingAlso 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 | 🟡 MinorUse 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 | 🟡 MinorArchitecture 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 | 🟡 MinorRoadmap 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 | 🟡 MinorPending 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 | 🟡 MinorConnector 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 | 🟡 MinorFix 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 | 🟡 MinorAdd a language tag to the directory-tree code fence.
Line 7 opens an unlabeled fenced block and triggers MD040. Use
textfor 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 | 🟡 MinorAdd 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 | 🟡 MinorFix 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 | 🟡 MinorSpecify 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 | 🟡 MinorAdd 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 | 🟡 MinorWrap underscore-heavy identifiers in backticks consistently.
Names like
_emit_codex_eventand_get_event_log_from_containershould 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 | 🟡 MinorUse “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 | 🟡 MinorTighten 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 | 🟡 MinorAdd a language tag to the fenced file-structure block.
Use a
textfence 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 | 🟡 MinorUpdate 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 | 🟡 MinorAdd an accessible label to the icon-only back button.
Line 21 renders a button with only an icon; please add
aria-labelso 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 | 🟡 MinorThis test doesn't actually verify studio-store precedence.
Both stores are populated here, but the only assertion is that
ReactFlowrendered. That would still pass if the component incorrectly kept usingeventKanbanTasks. Assert the selected task IDs/nodes, or spy onbuildDagNodes, 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 | 🟡 MinorUse concrete fakes here instead of
MagicMock.These helpers back the core payload assertions in this file, but
MagicMockmakes the doubles more permissive than the real task/session/container shapes. Small fake classes (ortypes.SimpleNamespacefor the container) would keep the tests stricter and easier to reason about.As per coding guidelines "Use stub/fake classes (e.g.,
_FakeLLMService) rather thanunittest.mockfor 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 | 🟡 MinorUse a breakpoint-aware divider here.
With
grid-cols-1, the first pane stacks above the second, so the always-onborder-rrenders on the outside edge instead of between sections. Switch that separator to a bottom border on mobile and a right border frommdupward.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 | 🟡 MinorThe 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 onsrc/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 | 🟡 MinorReset 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/agentTasksand 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 | 🟡 Minorv1.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 | 🟡 MinorFix 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 | 🟡 MinorTighten 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 | 🟡 MinorConvert 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 | 🟡 MinorFix 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 | 🟡 MinorAlign 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 | 🟡 MinorNormalize 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 | 🟡 MinorNormalize 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 | 🟡 MinorAvoid
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 stricttypeof 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 | 🟡 MinorAdd 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 | 🟡 MinorUse 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 | 🟡 MinorUse 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 | 🟡 MinorConvert 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/tsxorbash) 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 | 🟡 MinorEscape 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 | 🟡 MinorNormalize code block style to fenced blocks.
These sections trigger markdownlint
MD046due 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 | 🟡 MinorCode 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 | 🟡 MinorConvert this indented code section to fenced format.
markdownlint
MD046reports 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 | 🟡 MinorUse fenced code blocks instead of indented blocks.
markdownlint
MD046is 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 toenvironmentMatchGlobsfor future consistency.Currently, no component
*.spec.*files exist in the codebase, butcoverage.excludealready covers bothtestandspecpatterns. 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/webmakes 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
AgentStatusPanelprops can be wrapped withReadonly<>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 orreadonlymodifier 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_constantscan 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.
| --- | ||
| 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> |
There was a problem hiding this comment.
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}" |
There was a problem hiding this comment.
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).
| await _emit_codex_event( | ||
| "codex_dispatched", | ||
| task, | ||
| session, | ||
| {"assignee": task.assignee}, | ||
| ) |
There was a problem hiding this comment.
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.
web/src/app/agent-dashboard/page.tsx
Outdated
| 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} /> | ||
| )} |
There was a problem hiding this comment.
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.
web/src/lib/agent-events/dag.test.ts
Outdated
| 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) | ||
| }) |
There was a problem hiding this comment.
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.
web/src/lib/agent-events/dag.ts
Outdated
| 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 | ||
| } |
There was a problem hiding this comment.
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.
| 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.
🤖 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.
web/src/lib/agent-events/dag.ts
Outdated
| 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, | ||
| }) | ||
| } |
There was a problem hiding this comment.
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.
| 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.
web/src/lib/agent-events/parsers.ts
Outdated
| 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), |
There was a problem hiding this comment.
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,📝 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.
| 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.
[warning] 131-133: Extract this nested ternary operation into an independent statement.
🤖 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.
web/src/lib/agent-events/store.ts
Outdated
| 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 } |
There was a problem hiding this comment.
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.
| 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.
| 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) | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.ymlworkflow that runs backend and frontend tests with coverage, then submits results to SonarCloud - Extracted the inline backend offline test list from
ci.ymlinto a sharedscripts/ci/run_backend_offline_gates.shscript, reused by both CI and SonarCloud workflows - Configured coverage tooling:
pytest-covfor Python (coverage.xml),@vitest/coverage-v8for the web frontend (lcov.info), andsonar-project.propertiesfor 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 |
|
There was a problem hiding this comment.
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 inci.yml - Added coverage configuration for both Python (
pytest-cov,coverage.xml) and frontend (@vitest/coverage-v8,lcov.info) along withsonar-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.
| SONAR_ORGANIZATION: ${{ vars.SONAR_ORGANIZATION || 'jerry609' }} | ||
| SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY || 'jerry609_PaperBot' }} |
| - 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 |
| key: ${{ runner.os }}-sonar-cache | ||
| restore-keys: | | ||
| ${{ runner.os }}-sonar-cache |



Summary
Validation
Notes
Summary by CodeRabbit
New Features
Documentation
Tests