Commit graph

1 commit

Author SHA1 Message Date
Tejas Chopra
919379a8a1
fix(serena): stop the Serena dashboard popup and make --no-serena actually disable Serena (#1003)
## Description

Headroom installs the Serena MCP server by default during `headroom
wrap`, and many users reported the Serena web dashboard browser tab
popping up on every session — even when they never opted into Serena.
This PR fixes two distinct root causes: Serena's dashboard auto-open,
and `--no-serena` not actually disabling an already-installed Serena.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- `build_serena_spec()` now passes `--open-web-dashboard False` to
`serena start-mcp-server`. This is Serena's startup override for
`web_dashboard_open_on_launch` (`serena/mcp.py:317-318`), so it
suppresses the browser popup regardless of the user's
`~/.serena/serena_config.yml` — the correct fix is at the launch point,
not a per-machine config edit. The dashboard backend still runs and
stays reachable at `http://localhost:24282/dashboard/`; only the
auto-open is disabled. Applies to both launch paths (wrap + strands
bundle) since both go through `build_serena_spec()`.
- New `_disable_serena_mcp()`: `--no-serena` now actively removes the
Serena entry Headroom installed (ledger-verified) instead of merely
skipping registration. Previously a prior default wrap persisted a
`serena` entry and the agent kept launching it; the old `Skipping Serena
MCP` message was misleading. A user-managed Serena (absent from the
ledger) is reported and left untouched; an absent Serena prints the skip
message. Wired into both the Claude and Codex wrap paths.
- `unwrap_codex` now removes Headroom-installed Serena. Codex writes
Serena as its own `[mcp_servers.serena]` table, separate from the
provider block the config-restore handles, so a "cleaned" unwrap
previously left it behind (`unwrap_claude` already removed it; Codex was
the gap).
- Tests: updated `build_serena_spec` arg assertion + added a
no-popup-default test; new `test_serena_disable.py` covering
removed-when-headroom-owned, preserved-when-user-managed,
skip-when-absent, noop-when-undetected, and `unwrap_codex` removal.

## Testing

<!-- Check what you actually ran, then paste the real command output
below. -->

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [ ] Manual testing performed

### Test Output

```text
$ python -m pytest tests/test_cli/test_serena_disable.py tests/test_cli/test_wrap_codex.py tests/test_cli/test_unwrap_claude.py tests/test_mcp_registry/ -q
134 passed

$ python -m pytest tests/test_mcp_registry/test_install.py -q
... passed (build_serena_spec arg + no-popup-default assertions)

$ ruff check headroom/cli/wrap.py headroom/mcp_registry/install.py tests/test_cli/test_serena_disable.py tests/test_mcp_registry/test_install.py
All checks passed!

$ ruff format --check headroom/cli/wrap.py headroom/mcp_registry/install.py
... already formatted

$ mypy headroom/cli/wrap.py headroom/mcp_registry/install.py
Success: no issues found in 2 source files
```

## Real Behavior Proof

- Environment: macOS (darwin), Python 3.12 venv, Serena 1.5.4 cached via
uvx, headroom on branch fix/serena-no-dashboard-popup
- Exact command / steps: Traced Serena source — `serena/cli.py` exposes
`--open-web-dashboard <bool>`; `serena/mcp.py:317-318` sets
`config.web_dashboard_open_on_launch = open_web_dashboard`;
`serena/agent.py:706` feeds that to `DashboardManager`, which calls
`webbrowser.open()` (`serena/dashboard.py:831`). Verified click parses
`--open-web-dashboard False` → `False` via a CliRunner probe. Ran the
test suites above.
- Observed result: With the flag injected, the value that gates the
browser-open is forced to False at startup regardless of local config,
so no tab opens; dashboard backend still serves on its port.
`--no-serena` removes the previously-installed `serena` entry
(unregister called, "Removed previously-installed Serena MCP" printed)
and `unwrap codex` removes it too. All 134 targeted tests pass; ruff +
mypy clean.
- Not tested: A full end-to-end `headroom wrap claude` against a live
Claude Code install with a real browser was not run; verification is via
Serena source tracing + the click-parse probe + unit/integration tests
over the registrar and wrap/unwrap paths.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Additional Notes

Two unchecked checklist items are N/A: no user-facing docs reference the
Serena dashboard behavior, and CHANGELOG is generated via release-please
from the conventional commits. "Manual testing performed" is left
unchecked deliberately — see `Real Behavior Proof` → `Not tested` for
the exact boundary of what was and wasn't exercised against a live
browser.
2026-06-14 23:32:46 -07:00