Skip to content

feat: plan annotations, code review mode, E2E test scenarios in spec workflow#121

Merged
maxritter merged 5 commits intomainfrom
dev
Mar 27, 2026
Merged

feat: plan annotations, code review mode, E2E test scenarios in spec workflow#121
maxritter merged 5 commits intomainfrom
dev

Conversation

@maxritter
Copy link
Copy Markdown
Owner

@maxritter maxritter commented Mar 27, 2026

Summary

Single commit on top of main (7.7.6).

Console — Plan Annotation UI (Spec tab)

  • Block-based markdown rendering with native Selection API
  • Select text → write free-text note → auto-saves immediately (no Send Feedback button)
  • View/Review toggle in Specifications tab header

Console — Code Review Mode (Changes tab)

  • View/Review toggle in Changes page header
  • Inline diff annotations: click + on any hunk line, write note, auto-saves
  • Staged/unstaged counts in file list sidebar header

Console — Unified Annotation Storage

  • Single JSON per spec at docs/plans/.annotations/{plan}.json
  • Both planAnnotations and codeReviewAnnotations in one file
  • Removed feedback markdown file generation and all Send Feedback buttons

Console — Sidebar

  • Reorder: Dashboard → Changes → Specifications → Extensions → …

Spec Commands — E2E Test Scenarios

  • spec-plan Step 1.5.2: structured TS-NNN scenarios written during planning for UI features
  • spec-verify Step 3.9: executes TS-NNN scenarios with TaskCreate tracking, fix attempt limits, results written back to plan
  • spec-bugfix-plan/verify: lightweight Verification Scenario for UI-facing bugs

Spec Commands — Annotation JSON Integration

  • spec-plan/bugfix-plan read planAnnotations from unified JSON
  • spec-verify/bugfix-verify read codeReviewAnnotations from unified JSON

Launcher — Worktree Sync Auto-Stash

Rules — playwright-cli → agent-browser

  • pilot/rules/playwright-cli.md removed; pilot/rules/agent-browser.md added

Other

  • Enterprise Source Code License clause
  • Installer: kill stale Console worker on install
  • settings.json: autoDreamEnabled, agent-browser tip
  • README, Docusaurus, site: updated spec workflow descriptions

Summary by CodeRabbit

  • New Features

    • Headless mode (-p) for CI/CD/automation
    • Plan annotation workflow and inline code-review annotations (auto-saved, agent-aware)
    • E2E test scenarios executed via browser automation
    • New Enterprise tier: 100+ seats with source-code access
    • Free 7-day trial (no credit card)
  • Bug Fixes

    • More robust shell config handling for installer scripts
  • Chores

    • Migration from Playwright CLI to agent-browser
    • Installer process cleanup improvements
    • Updated docs/site content and removed Telegram integration docs

…workflow

Console — Plan Annotation UI (Spec tab):
- Block-based markdown rendering with native Selection API in Specifications tab
- Select any text in annotate mode → write free-text note → auto-saves to JSON immediately
- Single annotation type (free-text only — no DELETION/INSERTION/REPLACEMENT sub-types)
- Removed type toolbar and 'Add general comment' button
- View/Review toggle next to Specifications title; smart default (annotate for unapproved PENDING, view otherwise)
- AnnotationPanel, AnnotationToolbar, BlockRenderer, PlanAnnotator, useAnnotation, parser components

Console — Code Review Mode (Changes tab):
- View/Review toggle moved to Changes page header (previously buried in DiffPanel)
- Inline diff annotations: click '+' on any hunk line, write note, auto-saves to unified JSON
- Staged/unstaged counts in file list sidebar header
- CodeReviewPanel, CodeReviewWidget, useCodeReview components extracted

Console — Unified Annotation Storage:
- Single JSON per spec at docs/plans/.annotations/{plan-filename}.json
- Both planAnnotations and codeReviewAnnotations in one file
- Auto-save on every change (debounced) — no 'Send Feedback' button anywhere
- Removed feedback markdown file generation entirely
- AnnotationRoutes.ts and annotationStore.ts refactored to unified model
- Active spec resolution for Changes tab scopes code review annotations per spec

Console — Sidebar + Layout:
- Sidebar reorder: Dashboard → Changes → Specifications → Extensions → ...
- Changes and Specifications now adjacent for the plan→review→verify flow

Spec Commands — E2E Test Scenarios:
- spec-plan Step 1.5.2: write structured TS-NNN scenarios during planning for UI features
  (step table, priority, preconditions, mapped tasks — agent-browser executable)
- Task structure gains 'Mapped Scenarios' field; plan template gains '## E2E Test Scenarios' section
- spec-verify Step 3.9 refactored: executes plan's TS-NNN scenarios with TaskCreate tracking,
  fix attempts (max 2 → known issue escalation), results written to '## E2E Results' in plan file
- spec-bugfix-plan: Verification Scenario for UI-facing bugs (single TS-001 acceptance scenario)
- spec-bugfix-verify Step 3.5b: executes verification scenario, loops back on 2-attempt failure
- Console Spec view: E2E Test Scenarios, E2E Results, Verification Scenario added to DISPLAYED_SECTIONS with icons

Spec Commands — Annotation JSON Integration:
- spec-plan Step 1.7b and spec-bugfix-plan Step 1.4b read planAnnotations from unified JSON
- spec-verify Step 3.12b and spec-bugfix-verify Step 3.7b read codeReviewAnnotations from unified JSON
- All annotation feedback steps updated to DELETE via new /api/annotations endpoints

Launcher — Worktree Sync Auto-Stash:
- sync_worktree() now auto-stashes uncommitted changes on base branch before merging
- Stash is preserved (never dropped) if restore encounters conflicts
- spec-verify and spec-bugfix-verify: removed pre-sync working tree cleanliness check
- Fixes GitHub issue #119 ('Main branch has an unstashed file')

Rules + Commands — playwright-cli → agent-browser:
- pilot/rules/playwright-cli.md removed; pilot/rules/agent-browser.md added
- All spec commands, rules, and settings updated to reference agent-browser

Installer:
- finalize.py: kill stale Console worker (port 41777) on install so new files take effect immediately
- dependencies.py: agent-browser replaces playwright-cli in dependency checks
- shell_config.py: encoding improvements

Other:
- LICENSE: Enterprise Source Code License clause for 100+ seat organizations
- settings.json: autoDreamEnabled; removed CLAUDE_CODE_MAX_OUTPUT_TOKENS/EFFORT_LEVEL overrides; agent-browser tip
- pilot/ui/: viewer bundle rebuilt with annotation components (PlanAnnotator.js)
- README, Docusaurus, site: updated spec workflow descriptions, E2E scenarios, console features
- docs/docusaurus: remote-control.md removed, console.md expanded with annotation features
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 27, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pilot-shell Ignored Ignored Preview Mar 27, 2026 9:36am

Request Review

@claude
Copy link
Copy Markdown

claude bot commented Mar 27, 2026

Claude finished @maxritter's task in 2m 14s —— View job


Code Review — PR #121

  • Gather context
  • Read non-encrypted changed files
  • Post review feedback

Overview

Large but well-scoped PR. The annotation system, E2E scenario structure, and agent-browser migration are coherent and well-integrated across commands, rules, and installer. A few specific issues below.


Issues

1. install_agent_browser() silently ignores upgrade failure — installer/steps/dependencies.py

if _is_agent_browser_ready():
    # Upgrade existing installation
    _run_bash_with_retry("agent-browser upgrade", timeout=120)
    return True          # ← always True, even if upgrade fails

The upgrade result is discarded. If the intent is "upgrade failure is non-fatal, existing version still works," that's reasonable — but it should be explicit:

if _is_agent_browser_ready():
    _run_bash_with_retry("agent-browser upgrade", timeout=120)  # best-effort; ignore failure
    return True

Adding a comment would prevent future readers from treating this as a bug or accidentally adding if not around it. Fix this →


2. No tests for FinalizeStep._kill_stale_workerinstaller/steps/finalize.py

The new _kill_stale_worker() method has no test coverage. The existing finalize.py tests don't cover the new path. At minimum, a test verifying that exceptions are silently swallowed (so a missing lsof doesn't break install) and a test for the happy path (PIDs found and killed) would be useful.


3. sed regex is less robust than python3 for JSON path extraction — pilot/commands/spec-verify.md

- ~/.pilot/bin/pilot worktree cleanup --force --json <plan_slug> | python3 -c "import sys,json; print(json.load(sys.stdin)['project_root'])"
+ ~/.pilot/bin/pilot worktree cleanup --force --json <plan_slug> | sed -n 's/.*"project_root": *"\([^"]*\)".*/\1/p'

The sed regex fails silently for project root paths with backslashes, escaped quotes, or non-ASCII characters — sed can't handle JSON escape sequences. The original python3 approach is strictly correct. If python3 availability is the concern, jq (which Pilot already requires as a prerequisite) is a reliable alternative: | jq -r '.project_root'. Fix this →


4. CLAUDE_CODE_MAX_OUTPUT_TOKENS and CLAUDE_CODE_EFFORT_LEVEL removed without explanation — pilot/settings.json

-  "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "128000",
-  "CLAUDE_CODE_EFFORT_LEVEL": "high",

These are meaningful defaults that affect behavior. The PR description doesn't mention this change. If this is intentional (deferring to Claude Code defaults), a note in the PR description or a comment in the JSON (if the format supports it) would be helpful for future reference.


5. lsof not available on all Linux systems — installer/steps/finalize.py

_kill_stale_worker relies on lsof -ti :41777. On minimal Linux containers (devcontainers, some CI images), lsof may not be installed. The try/except catches this, so it won't break install — but the worker won't be killed. An alternative like fuser -k 41777/tcp or /proc enumeration would be more portable if this matters in practice.


Positive Highlights

  • shell_config.py encoding fixerrors="replace" on read_text() is correct and the new tests cover both the alias-present and alias-absent cases with non-UTF-8 bytes. Good defensive fix.

  • agent-browser.md rule — Comprehensive replacement for playwright-cli.md. The session isolation guidance (--session $PILOT_SESSION_ID) is well-motivated and the warning about bare commands causing cross-session interference is useful.

  • TS-NNN scenario structure — Clean design. Structured scenarios written during planning become an executable verification contract in spec-verify, with per-scenario TaskCreate tracking and explicit PASS/KNOWN_ISSUE outcomes written back to the plan. The fallback to ad-hoc verification when no scenarios exist is a good safety net.

  • Unified annotation JSON — Consolidating planAnnotations and codeReviewAnnotations into a single file per spec (docs/plans/.annotations/<plan>.json) is clean. The annotation feedback loops in spec-plan/spec-bugfix-plan (Step 1.7b / 1.4b) are well-integrated with the approval flow.

  • Worktree auto-stash — Fixes Main branch has an unstashed file - this error comes up often #119 cleanly. Removing the "pre-sync dirty check" that would block users with uncommitted changes is a good UX improvement now that auto-stash handles it.

  • Enterprise license clause — Well-scoped: internal use only, 100+ seat threshold, explicit carve-out from the general derivative works prohibition.


@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Walkthrough

Replaces Playwright CLI with agent-browser, adds Headless Mode and Console annotation/code-review workflows, introduces an Enterprise source-code license and pricing tier, updates installer/uninstall flows and tests, and adjusts site/docs content and git ignore/attributes.

Changes

Cohort / File(s) Summary
Git config & attributes
\.gitignore, docs/docusaurus/.gitignore, \.gitattributes
Added ignore patterns (spec-annotate, changes-header, docs/plans/.annotations/); swapped .playwright-cli/.agent-browser/; marked console/src/utils/logger.ts as !filter/!diff.
License & README
LICENSE, README.md
Added Section 2(e) Enterprise Source Code License and updated Section 4(b); updated README for Enterprise tier, 7‑day trial, headless CLI, dependency swap (playwright-cliagent-browser), and removed Telegram remote-control instructions.
Documentation (core features & workflows)
docs/docusaurus/docs/features/cli.md, docs/docusaurus/docs/features/console.md, docs/docusaurus/docs/features/open-source-tools.md, docs/docusaurus/docs/features/rules.md, docs/docusaurus/docs/features/extensions.md, docs/docusaurus/docs/features/remote-control.md, docs/docusaurus/docs/getting-started/installation.md, docs/docusaurus/docs/workflows/spec.md
Added Headless Mode docs (pilot -p), Plan Annotation and Code Review workflows, replaced Playwright references with agent-browser, removed Telegram integration docs, and documented E2E scenarios as the verification contract with automated browser execution and annotation handling.
Site UI components
docs/site/src/components/PricingSection.tsx, docs/site/src/components/WhatsInside.tsx, docs/site/src/components/ConsoleSection.tsx, docs/site/src/components/FAQSection.tsx, docs/site/src/components/ComparisonSection.tsx, docs/site/src/components/NavBar.tsx, docs/site/src/pages/Index.tsx
Added Enterprise pricing card and form URL constant; updated features list, icons, grid layout, slide metadata for console annotate/review, FAQ dependency text; removed ComparisonSection component and its nav link; reordered Index sections.
Installer — dependencies & tests
installer/steps/dependencies.py, installer/tests/unit/steps/test_dependencies.py, installer/tests/unit/steps/test_dependencies_playwright.py
Replaced Playwright install/readiness checks with agent-browser equivalents: _is_agent_browser_ready(), install_agent_browser() (npm global + agent-browser install/--with-deps), updated DependenciesStep to record agent_browser, and adjusted unit tests to mock new flows.
Installer — finalize & shell config
installer/steps/finalize.py, installer/steps/shell_config.py, installer/tests/unit/steps/test_shell_config.py, installer/tests/unit/steps/test_finalize.py
Added _kill_stale_worker() to terminate processes on port 41777 during finalize; changed shell-config reads to read_text(errors="replace") to tolerate non-UTF-8 bytes; added tests for alias handling with invalid bytes and for kill behavior.
Uninstall script
uninstall.sh
Updated uninstall instructions to remove agent-browser via npm uninstall -g agent-browser (removed Playwright uninstall line).

Sequence Diagram(s)

sequenceDiagram
  participant User as User (Console)
  participant Console as Console UI
  participant Agent as Agent
  participant Browser as agent-browser
  participant Plan as Plan storage (repo/file)

  User->>Console: Open spec (PENDING) and annotate
  Console->>Plan: Save annotations (auto-save)
  User->>Console: Submit for approval
  Console->>Agent: Send plan + annotations at approval checkpoint
  Agent->>Plan: Read plan and annotations
  Agent->>Browser: Execute E2E scenarios from plan
  Browser-->>Agent: Scenario results (pass/fail)
  Agent->>Plan: Write verification results (pass/fail, attempts)
  Agent->>Console: Request Code Review (opens Changes)
  User->>Console: Add inline diff annotations
  Console->>Plan: Persist diff annotations
  Console->>Agent: Notify agent, agent reads annotations, marks spec verified
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The PR includes extensive changes beyond the linked issue #119's scope: plan annotations, code review mode, E2E scenarios, licensing, and browser tool replacement. These appear intentional but lack explicit issue references. Clarify whether browser tool migration, console UI, licensing, and enterprise features were addressed in separate linked issues or are scope creep.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main features being added: plan annotations, code review mode, and E2E test scenarios integrated into the spec workflow.
Linked Issues check ✅ Passed The PR implements the auto-stash requirement from #119 by modifying sync_worktree() to auto-stash uncommitted changes before merging, fixing the recurring sync error issue.
Docstring Coverage ✅ Passed Docstring coverage is 96.67% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown

claude bot commented Mar 27, 2026

Claude finished @maxritter's task in 36s —— View job


Incremental Code Review — PR #121

The new push changed only console/src/utils/logger.ts, which is in the git-crypt encrypted console/** path. Per review configuration, this path is skipped entirely — no new issues to report for this push.


Still open from prior review

  1. install_agent_browser() discards upgrade result without comment — installer/steps/dependencies.py
  2. No tests for FinalizeStep._kill_stale_workerinstaller/steps/finalize.py
  3. sed used for JSON parsing instead of python3/jqpilot/commands/spec-verify.md
  4. CLAUDE_CODE_MAX_OUTPUT_TOKENS and CLAUDE_CODE_EFFORT_LEVEL removed without explanation — pilot/settings.json
  5. lsof portability on minimal Linux containers — installer/steps/finalize.py

No previously reported issues were resolved by this push.


Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (1)
installer/tests/unit/steps/test_dependencies.py (1)

36-66: ⚠️ Potential issue | 🟡 Minor

Add an explicit assertion for the new install_agent_browser path.

The patch target was migrated, but this test still doesn’t verify that the agent-browser installer is actually invoked during DependenciesStep.run. Also rename _mock_playwright to avoid stale naming.

Suggested test tightening
-        _mock_playwright,
+        mock_agent_browser,
         _mock_probe,
         _mock_rtk,
@@
             mock_python_tools.assert_called_once()
             mock_plugin_deps.assert_called_once()
+            mock_agent_browser.assert_called_once()

As per coding guidelines **/tests/**: Review test code briefly. Focus on: Test coverage for the feature being tested.

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

In `@installer/tests/unit/steps/test_dependencies.py` around lines 36 - 66, The
test test_dependencies_run_installs_core does not assert that
install_agent_browser is called and still uses a stale parameter name
(_mock_playwright); update the test to assert that install_agent_browser was
invoked when DependenciesStep.run executes and rename the corresponding patched
parameter from _mock_playwright to a meaningful name (e.g.,
mock_install_agent_browser) to match the patch target; ensure the assertion uses
the mock returned by the `@patch` of
installer.steps.dependencies.install_agent_browser and keep existing patches for
other installers intact.
🧹 Nitpick comments (1)
installer/steps/finalize.py (1)

53-53: Consider centralizing the port constant.

Port 41777 is hardcoded here and in other locations (e.g., pilot/hooks/_lib/dashboard_notify.py defines CONSOLE_URL = "http://localhost:41777"). Extracting this to a shared constant would reduce drift risk.

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

In `@installer/steps/finalize.py` at line 53, Multiple locations hardcode port
41777 (seen in the lsof call in finalize.py and CONSOLE_URL in
pilot/hooks/_lib/dashboard_notify.py); introduce a single shared constant (e.g.,
CONSOLE_PORT = 41777) in a common module and import it where needed, then update
the lsof invocation in finalize.py to use f":{CONSOLE_PORT}" and rebuild
CONSOLE_URL in dashboard_notify.py from that port (e.g., CONSOLE_URL =
f"http://localhost:{CONSOLE_PORT}") so all modules reference the same source of
truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/docusaurus/docs/features/console.md`:
- Around line 33-49: The text is inconsistent about whether annotations autosave
or require an explicit Save; update the Plan Annotation and Code Review sections
so the UX wording matches actual behavior by either removing "save automatically
as you write them" and clarifying that users must press the Save button, or by
removing the "press Save" mentions and documenting that annotations autosave;
specifically edit the sentences referencing "save automatically as you write
them" (Annotate mode / Plan Annotation), the phrase "press Save" in the Plan
Annotation paragraph, and the "press Save" in the Code Review paragraph (inline
annotation form under Review mode), and ensure the toggles "Annotate mode" and
"Review mode" and the inline "+" button description reflect the chosen behavior.

In `@docs/docusaurus/docs/workflows/spec.md`:
- Around line 70-72: The doc introduces a human "review" checkpoint (Review mode
/ Changes tab) but earlier text still claims "Approve" is the only manual step
and that "/spec" can run end-to-end when the three toggles are off; reconcile by
either making review optional or updating the contract to require it: if
mandatory, update earlier references of the manual step (the "Approve" step) and
the "/spec" description to state that a human review in Review mode is required
before marking a spec verified; if optional, clearly state here that the review
checkpoint is optional and document the new toggle (add a toggle name and
behavior) alongside the existing three toggles so the condition for fully
automated /spec runs is accurate (also update any mentions of "three toggles" to
include the new toggle or clarify its absence).

In `@docs/site/src/components/FAQSection.tsx`:
- Line 24: The sentence in FAQSection.tsx that currently asserts "No copyleft or
restrictive-licensed dependencies are introduced into your environment" is too
absolute and conflicts with other docs (e.g., Hypothesis under MPL-2.0). Update
the string in the FAQSection component to remove the absolute claim and instead
state that most bundled tools are under permissive licenses, while noting that
some included tools may use other open-source licenses (e.g., MPL-2.0) and to
refer to the open-source tools page for full license details; keep the change
localized to the quoted FAQ text.

In `@installer/steps/dependencies.py`:
- Around line 324-357: The current install_agent_browser returns immediately
after running "agent-browser upgrade" when _is_agent_browser_ready() is true,
skipping the necessary "agent-browser install" step; change
install_agent_browser so that if the tool is present you still run the upgrade
but then always run "agent-browser install" (use platform.system() to decide
whether to append "--with-deps" on Linux) and return the result of that final
_run_bash_with_retry call; keep using npm_global_cmd("npm install -g
agent-browser") and _run_bash_with_retry for the initial install path but ensure
the install step is executed unconditionally after upgrade/installation to
guarantee runtime readiness.

In `@installer/steps/finalize.py`:
- Around line 48-67: The _kill_stale_worker function uses Unix-only lsof/kill;
update it to be cross-platform by adding a platform check (use sys.platform or
os.name) and implement Windows logic (e.g., use psutil to find processes
listening on port 41777 or fallback to subprocess for Unix only). Mirror the
graceful termination pattern used in dependencies.py::_kill_proc(): attempt a
graceful terminate/terminate-equivalent first with a short timeout, then
escalate to forceful kill if still alive. Ensure you reference
_kill_stale_worker when changing the port-scanning and termination logic and
keep the Unix lsof/kill branch only under the appropriate platform guard.

In `@installer/tests/unit/steps/test_dependencies_playwright.py`:
- Around line 98-106: The test should lock down the short-circuit by making the
patched _run_bash_with_retry explicitly fail on the first call and then
asserting no subsequent install call was attempted: set mock_run.side_effect to
fail on the first invocation (e.g., [False]) and then assert mock_run was called
exactly once and that that single call was the npm install command passed into
_run_bash_with_retry; keep the existing patches for _is_agent_browser_ready and
_run_bash_with_retry and update test_returns_false_when_npm_fails to verify only
the first (npm) command was executed and no second install step was invoked.

---

Outside diff comments:
In `@installer/tests/unit/steps/test_dependencies.py`:
- Around line 36-66: The test test_dependencies_run_installs_core does not
assert that install_agent_browser is called and still uses a stale parameter
name (_mock_playwright); update the test to assert that install_agent_browser
was invoked when DependenciesStep.run executes and rename the corresponding
patched parameter from _mock_playwright to a meaningful name (e.g.,
mock_install_agent_browser) to match the patch target; ensure the assertion uses
the mock returned by the `@patch` of
installer.steps.dependencies.install_agent_browser and keep existing patches for
other installers intact.

---

Nitpick comments:
In `@installer/steps/finalize.py`:
- Line 53: Multiple locations hardcode port 41777 (seen in the lsof call in
finalize.py and CONSOLE_URL in pilot/hooks/_lib/dashboard_notify.py); introduce
a single shared constant (e.g., CONSOLE_PORT = 41777) in a common module and
import it where needed, then update the lsof invocation in finalize.py to use
f":{CONSOLE_PORT}" and rebuild CONSOLE_URL in dashboard_notify.py from that port
(e.g., CONSOLE_URL = f"http://localhost:{CONSOLE_PORT}") so all modules
reference the same source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c13199a6-31a9-4701-a0a4-d84be1f96237

📥 Commits

Reviewing files that changed from the base of the PR and between 641c0e4 and e4b78f5.

⛔ Files ignored due to path filters (65)
  • console/package.json is excluded by !console/**
  • console/src/cli/handlers/user-message.ts is excluded by !console/**
  • console/src/services/worker-service.ts is excluded by !console/**
  • console/src/services/worker/http/routes/AnnotationRoutes.ts is excluded by !console/**
  • console/src/services/worker/http/routes/LicenseRoutes.ts is excluded by !console/**
  • console/src/services/worker/http/routes/PlanRoutes.ts is excluded by !console/**
  • console/src/services/worker/http/routes/ViewerRoutes.ts is excluded by !console/**
  • console/src/services/worker/http/routes/utils/annotationStore.ts is excluded by !console/**
  • console/src/services/worker/http/routes/utils/planFileReader.ts is excluded by !console/**
  • console/src/ui/viewer/components/LicenseBadge.tsx is excluded by !console/**
  • console/src/ui/viewer/hooks/useLicense.ts is excluded by !console/**
  • console/src/ui/viewer/layouts/Sidebar/SidebarNav.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Changes/DiffPanel.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Changes/index.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Changes/review/CodeReviewPanel.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Changes/review/CodeReviewWidget.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Changes/review/types.ts is excluded by !console/**
  • console/src/ui/viewer/views/Changes/review/useCodeReview.ts is excluded by !console/**
  • console/src/ui/viewer/views/Spec/SpecHeaderCard.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Spec/SpecSection.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/AnnotationPanel.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/AnnotationToolbar.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/BlockRenderer.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/PlanAnnotator.tsx is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/index.ts is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/parser.ts is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/types.ts is excluded by !console/**
  • console/src/ui/viewer/views/Spec/annotation/useAnnotation.ts is excluded by !console/**
  • console/src/ui/viewer/views/Spec/index.tsx is excluded by !console/**
  • console/tests/annotation/code-review.test.ts is excluded by !console/**
  • console/tests/annotation/parser.test.ts is excluded by !console/**
  • console/tests/annotation/use-annotation.test.ts is excluded by !console/**
  • console/tests/server/license-routes.test.ts is excluded by !console/**
  • console/tests/ui/ChangesNavigation.test.ts is excluded by !console/**
  • console/tests/ui/license-badge.test.ts is excluded by !console/**
  • console/tests/worker/annotation-routes.test.ts is excluded by !console/**
  • console/tests/worker/license-routes.test.ts is excluded by !console/**
  • console/tests/worker/utils/isValidPlanPath.test.ts is excluded by !console/**
  • launcher/cli.py is excluded by !launcher/**
  • launcher/tests/unit/test_cli.py is excluded by !launcher/**
  • launcher/tests/unit/test_worktree.py is excluded by !launcher/**
  • launcher/tests/unit/test_wrapper.py is excluded by !launcher/**
  • launcher/worktree.py is excluded by !launcher/**
  • launcher/wrapper.py is excluded by !launcher/**
  • pilot/commands/create-skill.md is excluded by !pilot/**
  • pilot/commands/spec-bugfix-plan.md is excluded by !pilot/**
  • pilot/commands/spec-bugfix-verify.md is excluded by !pilot/**
  • pilot/commands/spec-implement.md is excluded by !pilot/**
  • pilot/commands/spec-plan.md is excluded by !pilot/**
  • pilot/commands/spec-verify.md is excluded by !pilot/**
  • pilot/commands/spec.md is excluded by !pilot/**
  • pilot/rules/agent-browser.md is excluded by !pilot/**
  • pilot/rules/development-practices.md is excluded by !pilot/**
  • pilot/rules/playwright-cli.md is excluded by !pilot/**
  • pilot/rules/standards-backend.md is excluded by !pilot/**
  • pilot/rules/standards-frontend.md is excluded by !pilot/**
  • pilot/rules/task-and-workflow.md is excluded by !pilot/**
  • pilot/rules/testing.md is excluded by !pilot/**
  • pilot/rules/verification.md is excluded by !pilot/**
  • pilot/scripts/mcp-server.cjs is excluded by !pilot/**
  • pilot/scripts/worker-service.cjs is excluded by !pilot/**
  • pilot/settings.json is excluded by !pilot/**
  • pilot/ui/PlanAnnotator.js is excluded by !pilot/**
  • pilot/ui/viewer-bundle.js is excluded by !pilot/**
  • pilot/ui/viewer.css is excluded by !pilot/**
📒 Files selected for processing (23)
  • .gitignore
  • LICENSE
  • README.md
  • docs/docusaurus/.gitignore
  • docs/docusaurus/docs/features/cli.md
  • docs/docusaurus/docs/features/console.md
  • docs/docusaurus/docs/features/extensions.md
  • docs/docusaurus/docs/features/open-source-tools.md
  • docs/docusaurus/docs/features/remote-control.md
  • docs/docusaurus/docs/features/rules.md
  • docs/docusaurus/docs/getting-started/installation.md
  • docs/docusaurus/docs/workflows/spec.md
  • docs/site/src/components/ConsoleSection.tsx
  • docs/site/src/components/FAQSection.tsx
  • docs/site/src/components/PricingSection.tsx
  • docs/site/src/components/WhatsInside.tsx
  • installer/steps/dependencies.py
  • installer/steps/finalize.py
  • installer/steps/shell_config.py
  • installer/tests/unit/steps/test_dependencies.py
  • installer/tests/unit/steps/test_dependencies_playwright.py
  • installer/tests/unit/steps/test_shell_config.py
  • uninstall.sh
💤 Files with no reviewable changes (1)
  • docs/docusaurus/docs/features/remote-control.md

Comment on lines +324 to +357
def _is_agent_browser_ready() -> bool:
"""Check if agent-browser is installed and Chrome is available."""
if not command_exists("agent-browser"):
return False

for cache_dir in _get_playwright_cache_dirs():
if not cache_dir.exists():
continue

for chromium_dir in cache_dir.glob("chromium-*"):
if (chromium_dir / "INSTALLATION_COMPLETE").exists():
return True

for chromium_dir in cache_dir.glob("chromium_headless_shell-*"):
if (chromium_dir / "INSTALLATION_COMPLETE").exists():
return True

return False


def _install_playwright_system_deps() -> bool:
"""Install OS-level system dependencies required by Playwright browsers.

Runs 'npx playwright install-deps' which installs system libraries
(libglib, libatk, etc.) needed by Chromium. Required on Linux/devcontainers,
no-op on macOS.
"""
cmd = ["npx", "-y", "playwright", "install-deps"]
for attempt in range(MAX_RETRIES):
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode == 0:
return True
except Exception:
pass
if attempt < MAX_RETRIES - 1:
time.sleep(RETRY_DELAY)
return False
try:
result = subprocess.run(
["agent-browser", "--version"],
capture_output=True,
text=True,
timeout=10,
)
return result.returncode == 0
except Exception:
return False


def install_playwright_cli() -> bool:
"""Install playwright-cli for headless browser automation.
def install_agent_browser() -> bool:
"""Install agent-browser for headless browser automation.

On Linux ARM64, installs chromium specifically (Chrome has no ARM64 builds).
On Linux, installs with --with-deps for system dependencies.
"""
if _is_playwright_cli_ready():
return True

if not _run_bash_with_retry(npm_global_cmd("npm install -g @playwright/cli@latest")):
return False
import platform

if _is_playwright_cli_ready():
_install_playwright_system_deps()
if _is_agent_browser_ready():
# Upgrade existing installation
_run_bash_with_retry("agent-browser upgrade", timeout=120)
return True

install_cmd = ["playwright-cli", "install", "chromium"] if is_linux_arm64() else ["playwright-cli", "install"]

for attempt in range(MAX_RETRIES):
try:
result = subprocess.run(install_cmd, capture_output=True, text=True, timeout=300)
if result.returncode == 0:
break
except Exception:
pass
if attempt < MAX_RETRIES - 1:
time.sleep(RETRY_DELAY)
continue
if not _run_bash_with_retry(npm_global_cmd("npm install -g agent-browser")):
return False

_install_playwright_system_deps()
return True
install_cmd = "agent-browser install --with-deps" if platform.system() == "Linux" else "agent-browser install"
return _run_bash_with_retry(install_cmd, timeout=300)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In the official agent-browser CLI docs, does agent-browser upgradeguarantee browser binaries/system dependencies are installed, or isagent-browser install still required to ensure runtime readiness?

💡 Result:

No, agent-browser upgrade does not guarantee that browser binaries/system dependencies are installed. agent-browser install is still required to ensure runtime readiness.

Citations:


Ensure agent-browser install runs unconditionally to guarantee runtime readiness.

The current implementation returns success immediately after agent-browser upgrade if the tool is already installed, skipping the agent-browser install step. However, agent-browser upgrade does not guarantee that browser binaries and system dependencies are installed—agent-browser install is still required for runtime readiness. This violates the idempotency requirement (installer must be safely re-runnable).

Refactor to always call agent-browser install after upgrade, even when the tool already exists:

Fix
 def install_agent_browser() -> bool:
     if _is_agent_browser_ready():
-        # Upgrade existing installation
+        # Best-effort upgrade; continue to install to guarantee runtime assets
         _run_bash_with_retry("agent-browser upgrade", timeout=120)
-        return True
-
-    if not _run_bash_with_retry(npm_global_cmd("npm install -g agent-browser")):
-        return False
+    else:
+        if not _run_bash_with_retry(npm_global_cmd("npm install -g agent-browser")):
+            return False
 
     install_cmd = "agent-browser install --with-deps" if platform.system() == "Linux" else "agent-browser install"
     return _run_bash_with_retry(install_cmd, timeout=300)
🧰 Tools
🪛 Ruff (0.15.7)

[error] 331-331: Starting a process with a partial executable path

(S607)


[warning] 337-337: Do not catch blind exception: Exception

(BLE001)

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

In `@installer/steps/dependencies.py` around lines 324 - 357, The current
install_agent_browser returns immediately after running "agent-browser upgrade"
when _is_agent_browser_ready() is true, skipping the necessary "agent-browser
install" step; change install_agent_browser so that if the tool is present you
still run the upgrade but then always run "agent-browser install" (use
platform.system() to decide whether to append "--with-deps" on Linux) and return
the result of that final _run_bash_with_retry call; keep using
npm_global_cmd("npm install -g agent-browser") and _run_bash_with_retry for the
initial install path but ensure the install step is executed unconditionally
after upgrade/installation to guarantee runtime readiness.

Comment on lines +48 to +67
@staticmethod
def _kill_stale_worker() -> None:
"""Kill any running Console worker so it restarts with the newly installed files."""
try:
result = subprocess.run(
["lsof", "-ti", ":41777"],
capture_output=True,
text=True,
timeout=5,
)
pids = result.stdout.strip()
if pids:
for pid in pids.splitlines():
subprocess.run(
["kill", "-9", pid.strip()],
capture_output=True,
timeout=5,
)
except Exception:
pass
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Cross-platform compatibility: lsof and kill are Unix-only.

This implementation won't work on Windows since lsof and kill are Unix utilities. Consider adding a platform check or using a cross-platform approach.

🛠️ Suggested platform guard
+import platform
+
 `@staticmethod`
 def _kill_stale_worker() -> None:
     """Kill any running Console worker so it restarts with the newly installed files."""
+    if platform.system() == "Windows":
+        return  # lsof/kill not available on Windows
     try:
         result = subprocess.run(

Additionally, consider using graceful termination (SIGTERM) before SIGKILL, similar to _kill_proc() in dependencies.py which uses terminate() with a timeout before escalating to kill(). This gives the worker a chance to clean up.

As per coding guidelines: "Review installer code for: Cross-platform compatibility".

📝 Committable suggestion

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

Suggested change
@staticmethod
def _kill_stale_worker() -> None:
"""Kill any running Console worker so it restarts with the newly installed files."""
try:
result = subprocess.run(
["lsof", "-ti", ":41777"],
capture_output=True,
text=True,
timeout=5,
)
pids = result.stdout.strip()
if pids:
for pid in pids.splitlines():
subprocess.run(
["kill", "-9", pid.strip()],
capture_output=True,
timeout=5,
)
except Exception:
pass
import platform
`@staticmethod`
def _kill_stale_worker() -> None:
"""Kill any running Console worker so it restarts with the newly installed files."""
if platform.system() == "Windows":
return # lsof/kill not available on Windows
try:
result = subprocess.run(
["lsof", "-ti", ":41777"],
capture_output=True,
text=True,
timeout=5,
)
pids = result.stdout.strip()
if pids:
for pid in pids.splitlines():
subprocess.run(
["kill", "-9", pid.strip()],
capture_output=True,
timeout=5,
)
except Exception:
pass
🧰 Tools
🪛 Ruff (0.15.7)

[error] 53-53: Starting a process with a partial executable path

(S607)


[error] 61-61: subprocess call: check for execution of untrusted input

(S603)


[error] 62-62: Starting a process with a partial executable path

(S607)


[error] 66-67: try-except-pass detected, consider logging the exception

(S110)


[warning] 66-66: Do not catch blind exception: Exception

(BLE001)

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

In `@installer/steps/finalize.py` around lines 48 - 67, The _kill_stale_worker
function uses Unix-only lsof/kill; update it to be cross-platform by adding a
platform check (use sys.platform or os.name) and implement Windows logic (e.g.,
use psutil to find processes listening on port 41777 or fallback to subprocess
for Unix only). Mirror the graceful termination pattern used in
dependencies.py::_kill_proc(): attempt a graceful terminate/terminate-equivalent
first with a short timeout, then escalate to forceful kill if still alive.
Ensure you reference _kill_stale_worker when changing the port-scanning and
termination logic and keep the Unix lsof/kill branch only under the appropriate
platform guard.

Comment on lines +98 to +106
@patch("installer.steps.dependencies._is_agent_browser_ready")
@patch("installer.steps.dependencies._run_bash_with_retry")
def test_returns_false_when_browser_install_fails(
self, mock_run, mock_ready, mock_subprocess, mock_deps, _mock_sleep
):
"""Returns False when playwright-cli install fails."""
from installer.steps.dependencies import install_playwright_cli
def test_returns_false_when_npm_fails(self, mock_run, mock_ready):
"""Returns False when npm install fails."""
from installer.steps.dependencies import install_agent_browser

mock_ready.side_effect = [False, False]
mock_run.return_value = True
mock_result = MagicMock()
mock_result.returncode = 1
mock_subprocess.run.return_value = mock_result
assert install_playwright_cli() is False
mock_deps.assert_not_called()
mock_ready.return_value = False
mock_run.return_value = False
assert install_agent_browser() is False
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Lock down the npm-failure short-circuit.

With mock_run.return_value = False, this still passes even if install_agent_browser() incorrectly attempts the second install step after the npm failure. Patch the platform/npm helpers here and assert that only the first command ran.

As per coding guidelines, **/tests/** should focus on test coverage for the feature being tested and proper mocking of external dependencies.

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

In `@installer/tests/unit/steps/test_dependencies_playwright.py` around lines 98 -
106, The test should lock down the short-circuit by making the patched
_run_bash_with_retry explicitly fail on the first call and then asserting no
subsequent install call was attempted: set mock_run.side_effect to fail on the
first invocation (e.g., [False]) and then assert mock_run was called exactly
once and that that single call was the npm install command passed into
_run_bash_with_retry; keep the existing patches for _is_agent_browser_ready and
_run_bash_with_retry and update test_returns_false_when_npm_fails to verify only
the first (npm) command was executed and no second install step was invoked.

@claude
Copy link
Copy Markdown

claude bot commented Mar 27, 2026

Claude finished @maxritter's task in 2m 5s —— View job


Incremental Code Review — PR #121

  • Gather context
  • Read changed non-encrypted files
  • Post incremental review feedback

Resolved by this push

install_agent_browser() upgrade commentinstaller/steps/dependencies.py:349 now has the comment # best-effort; failure is non-fatal — existing version still works. Issue #1 from prior review closed.

✅ Tests for _kill_stale_workerinstaller/tests/unit/steps/test_finalize.py now has TestKillStaleWorker with three tests: happy path (PIDs found and killed), no-PIDs path, and FileNotFoundError swallowing when lsof is missing. Issue #2 from prior review closed.

jq replaces sed for JSON parsingpilot/commands/spec-verify.md:373 now uses jq -r '.project_root' for the cleanup pipe. Issue #3 from prior review closed.

spec.md Code Review checkpoint now documented — Line 26 now reads "Manual steps are Approve (required) and Code Review (optional, via Console)." CodeRabbit's reconciliation issue closed.

FAQSection.tsx copyleft claim softened — The answer now explicitly calls out hypothesis as MPL-2.0 and narrows the claim to "no GPL or AGPL dependencies." CodeRabbit's major issue closed.


New issue introduced in this push

console.md:49 — "press Save" still present for Code Review annotations

The Plan Annotation section was fixed (no "press Save" anymore, annotations are "immediately saved"). But the Code Review section still reads:

write your note and press Save

The PR description says code review annotations also "auto-saves" (inline annotation form). If that's accurate, this sentence is still inconsistent with the Plan Annotation wording on line 33 ("Annotations save automatically as you write them"). Fix this →


Still open from prior reviews

  1. CLAUDE_CODE_MAX_OUTPUT_TOKENS and CLAUDE_CODE_EFFORT_LEVEL removed without explanation — pilot/settings.json (not touched in this push)
  2. lsof/kill in _kill_stale_worker have no platform guard for Windows — installer/steps/finalize.py:48-67
  3. install_agent_browser() skips agent-browser install for existing installs, only running upgrade — installer/steps/dependencies.py:348-350 (CodeRabbit major finding — browser binaries not guaranteed to be installed on upgrade-only path)
  4. test_dependencies.py stale _mock_playwright param name and missing assert_called_once() for install_agent_browser (CodeRabbit)
  5. test_dependencies_playwright.py npm-failure short-circuit not locked down with assert_called_once (CodeRabbit)

@claude
Copy link
Copy Markdown

claude bot commented Mar 27, 2026

Claude finished @maxritter's task in 57s —— View job


Incremental Code Review — PR #121

  • Read changed non-encrypted files (.gitattributes, pilot/ui/viewer.css)
  • Post incremental review feedback

No new issues in this push

.gitattributes — Adding console/src/utils/logger.ts !filter !diff correctly exempts logger.ts from git-crypt, consistent with the CI fix described in the recent commits. No issues.

pilot/ui/viewer.css — Minified build artifact, +1/-1 update reflecting the new annotation/code review UI styles. No issues reviewable from the diff.

console/src/utils/logger.ts — Encrypted path, skipped per review config.


Still open from prior reviews

  1. console.md:49 — "press Save" wording in Code Review annotations section inconsistent with autosave behavior described on line 33
  2. CLAUDE_CODE_MAX_OUTPUT_TOKENS and CLAUDE_CODE_EFFORT_LEVEL removed without explanation — pilot/settings.json
  3. lsof/kill in _kill_stale_worker has no platform guard for Windows — installer/steps/finalize.py
  4. install_agent_browser() skips agent-browser install for existing installs (upgrade-only path doesn't guarantee browser binaries) — installer/steps/dependencies.py
  5. test_dependencies.py stale _mock_playwright param name, missing assert_called_once() for install_agent_browser — (CodeRabbit)
  6. test_dependencies_playwright.py npm-failure short-circuit not locked down with assert_called_once — (CodeRabbit)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
installer/steps/dependencies.py (1)

341-356: ⚠️ Potential issue | 🟠 Major

agent-browser install is skipped when tool already exists, breaking idempotency.

The function returns immediately after agent-browser upgrade (line 349-350) without running agent-browser install, which is required to ensure browser binaries and system dependencies are present. Per coding guidelines, installer code must be safely re-runnable.

Proposed fix
 def install_agent_browser() -> bool:
     """Install agent-browser for headless browser automation.

     On Linux, installs with --with-deps for system dependencies.
     """
     import platform

     if _is_agent_browser_ready():
-        _run_bash_with_retry("agent-browser upgrade", timeout=120)  # best-effort; failure is non-fatal — existing version still works
-        return True
+        _run_bash_with_retry("agent-browser upgrade", timeout=120)  # best-effort upgrade
+    else:
+        if not _run_bash_with_retry(npm_global_cmd("npm install -g agent-browser")):
+            return False

-    if not _run_bash_with_retry(npm_global_cmd("npm install -g agent-browser")):
-        return False
-
     install_cmd = "agent-browser install --with-deps" if platform.system() == "Linux" else "agent-browser install"
     return _run_bash_with_retry(install_cmd, timeout=300)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@installer/steps/dependencies.py` around lines 341 - 356, The current
install_agent_browser function returns immediately after calling
_run_bash_with_retry("agent-browser upgrade") when _is_agent_browser_ready() is
true, which skips the required "agent-browser install" step; change the control
flow so that when _is_agent_browser_ready() is true you still run the install
step (to ensure browser binaries/deps) instead of returning early — e.g., call
_run_bash_with_retry("agent-browser upgrade") for best-effort upgrade, then
compute install_cmd (using platform.system()) and call
_run_bash_with_retry(install_cmd, timeout=300); keep the existing
npm_global_cmd("npm install -g agent-browser") path for the first-time install
and preserve timeouts and return boolean results from _run_bash_with_retry.
🧹 Nitpick comments (2)
docs/site/src/components/WhatsInside.tsx (1)

123-124: Duplicate hover:border-primary/50 class.

The class hover:border-primary/50 appears twice on line 124. This is harmless but redundant.

🧹 Proposed fix
-                className={`group relative rounded-lg p-5 border border-border/50 bg-card
-                  hover:border-primary/50 hover:bg-card hover:border-primary/50
+                className={`group relative rounded-lg p-5 border border-border/50 bg-card
+                  hover:border-primary/50 hover:bg-card
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/site/src/components/WhatsInside.tsx` around lines 123 - 124, Duplicate
Tailwind class in the JSX className prop inside the WhatsInside component:
remove the redundant "hover:border-primary/50" entry so the template string for
the element's className only contains a single instance of that class; edit the
className in the JSX (the template string shown) to delete the duplicate token.
installer/tests/unit/steps/test_finalize.py (1)

90-108: Good test coverage; consider verifying the lsof call arguments.

The tests cover the main scenarios well (multiple PIDs, empty result, lsof missing). One minor gap: test_kills_pids_found_by_lsof verifies the kill calls but doesn't confirm the lsof call used the correct arguments ["lsof", "-ti", ":41777"].

💡 Optional: Add lsof call verification
         assert mock_run.call_count == 3
+        mock_run.assert_any_call(
+            ["lsof", "-ti", ":41777"],
+            capture_output=True,
+            text=True,
+            timeout=5,
+        )
         mock_run.assert_any_call(["kill", "-9", "1234"], capture_output=True, timeout=5)
         mock_run.assert_any_call(["kill", "-9", "5678"], capture_output=True, timeout=5)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@installer/tests/unit/steps/test_finalize.py` around lines 90 - 108, The test
TestKillStaleWorker.test_kills_pids_found_by_lsof currently asserts kill calls
but doesn't verify the initial lsof invocation; update the test to also assert
that subprocess.run was called for the lsof command with the exact args ["lsof",
"-ti", ":41777"] (i.e. the first call to subprocess.run when invoking
FinalizeStep._kill_stale_worker) and preserve existing assertions for the kill
calls, referencing FinalizeStep._kill_stale_worker and the patched
subprocess.run mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@installer/steps/dependencies.py`:
- Around line 341-356: The current install_agent_browser function returns
immediately after calling _run_bash_with_retry("agent-browser upgrade") when
_is_agent_browser_ready() is true, which skips the required "agent-browser
install" step; change the control flow so that when _is_agent_browser_ready() is
true you still run the install step (to ensure browser binaries/deps) instead of
returning early — e.g., call _run_bash_with_retry("agent-browser upgrade") for
best-effort upgrade, then compute install_cmd (using platform.system()) and call
_run_bash_with_retry(install_cmd, timeout=300); keep the existing
npm_global_cmd("npm install -g agent-browser") path for the first-time install
and preserve timeouts and return boolean results from _run_bash_with_retry.

---

Nitpick comments:
In `@docs/site/src/components/WhatsInside.tsx`:
- Around line 123-124: Duplicate Tailwind class in the JSX className prop inside
the WhatsInside component: remove the redundant "hover:border-primary/50" entry
so the template string for the element's className only contains a single
instance of that class; edit the className in the JSX (the template string
shown) to delete the duplicate token.

In `@installer/tests/unit/steps/test_finalize.py`:
- Around line 90-108: The test TestKillStaleWorker.test_kills_pids_found_by_lsof
currently asserts kill calls but doesn't verify the initial lsof invocation;
update the test to also assert that subprocess.run was called for the lsof
command with the exact args ["lsof", "-ti", ":41777"] (i.e. the first call to
subprocess.run when invoking FinalizeStep._kill_stale_worker) and preserve
existing assertions for the kill calls, referencing
FinalizeStep._kill_stale_worker and the patched subprocess.run mock.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 024c0423-239b-4211-bda3-82f3c9717336

📥 Commits

Reviewing files that changed from the base of the PR and between e4b78f5 and bc620e9.

⛔ Files ignored due to path filters (4)
  • console/package.json is excluded by !console/**
  • console/src/utils/logger.ts is excluded by !console/**
  • pilot/commands/spec-verify.md is excluded by !pilot/**
  • pilot/ui/viewer.css is excluded by !pilot/**
📒 Files selected for processing (10)
  • .gitattributes
  • docs/docusaurus/docs/features/console.md
  • docs/docusaurus/docs/workflows/spec.md
  • docs/site/src/components/ComparisonSection.tsx
  • docs/site/src/components/FAQSection.tsx
  • docs/site/src/components/NavBar.tsx
  • docs/site/src/components/WhatsInside.tsx
  • docs/site/src/pages/Index.tsx
  • installer/steps/dependencies.py
  • installer/tests/unit/steps/test_finalize.py
💤 Files with no reviewable changes (2)
  • docs/site/src/components/NavBar.tsx
  • docs/site/src/components/ComparisonSection.tsx
✅ Files skipped from review due to trivial changes (1)
  • .gitattributes
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/docusaurus/docs/features/console.md
  • docs/site/src/components/FAQSection.tsx
  • docs/docusaurus/docs/workflows/spec.md

@maxritter maxritter merged commit e5ad5ea into main Mar 27, 2026
15 checks passed
github-actions bot pushed a commit that referenced this pull request Mar 27, 2026
# [7.8.0](v7.7.6...v7.8.0) (2026-03-27)

### Features

* plan annotations, code review mode, E2E test scenarios in spec workflow ([#121](#121)) ([e5ad5ea](e5ad5ea)), closes [#119](#119)
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 7.8.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@maxritter maxritter deleted the dev branch March 28, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Main branch has an unstashed file - this error comes up often

1 participant