Disambiguate project profiles with the same basename (#74) #86

Merged
the_og merged 1 commits from fix/74-project-label-collision into main 2026-08-03 23:42:19 -04:00
Owner

Closes #74.

Two repos both named app (~/work/app/.mcp.json, ~/personal/app/.mcp.json) both rendered as "Project: app" in the picker — the paths dedupe fine, but the labels were indistinguishable, so picking the wrong one meant editing/backing-up/writing the wrong repo's config. api/web/app/server/client make this common.

The fix (pure + tested):

  • disambiguate_project_labels(dirs) — labels stay Project: <name> until a basename collides, then only the colliding ones widen toward the root a component at a time (Project: work/app vs Project: personal/app), widening further if the parent also collides. No-collision case unchanged.
  • Full path in the combo item's ToolTipRole so a profile is always verifiable on hover.
  • Tightened discovery: the docstring claimed a top-level object but the code only checked is_file(), so a .mcp.json that was a JSON array or garbage still became a profile and only failed on open. It now must strict-parse to a dict, else it's skipped.

Tests: +6 (basename / one-level widen / deeper widen, plus discover_project_configs disambiguation and skipping array/garbage/missing). 463 passed, ruff clean. GUI change is one setItemData tooltip line.

Closes #74. Two repos both named `app` (`~/work/app/.mcp.json`, `~/personal/app/.mcp.json`) both rendered as **"Project: app"** in the picker — the paths dedupe fine, but the labels were indistinguishable, so picking the wrong one meant editing/backing-up/writing the wrong repo's config. `api`/`web`/`app`/`server`/`client` make this common. **The fix (pure + tested):** - `disambiguate_project_labels(dirs)` — labels stay `Project: <name>` until a basename collides, then **only the colliding ones** widen toward the root a component at a time (`Project: work/app` vs `Project: personal/app`), widening further if the parent also collides. No-collision case unchanged. - Full path in the combo item's `ToolTipRole` so a profile is always verifiable on hover. - Tightened discovery: the docstring claimed a top-level object but the code only checked `is_file()`, so a `.mcp.json` that was a JSON array or garbage still became a profile and only failed on open. It now must strict-parse to a dict, else it's skipped. **Tests:** +6 (basename / one-level widen / deeper widen, plus `discover_project_configs` disambiguation and skipping array/garbage/missing). 463 passed, ruff clean. GUI change is one `setItemData` tooltip line.
the_og added 1 commit 2026-08-03 23:29:26 -04:00
fix: disambiguate project profiles that share a directory basename (#74)
CI / Lint (ruff) (pull_request) Successful in 12s
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 22s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 19s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 19s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 19s
CI / Catalog signature (pull_request) Successful in 11s
0191a93eb9
discover_project_configs labelled every project by basename alone, so
~/work/app/.mcp.json and ~/personal/app/.mcp.json both read as
"Project: app". Paths dedupe correctly, so both profiles existed -- they
were just indistinguishable in the picker, and picking the wrong one meant
editing, backing up, and writing the wrong repo's config. api/web/app/
server/client as repo names make this common.

- disambiguate_project_labels(dirs): pure, testable. Labels stay
  "Project: <name>" until a basename collides, then only the colliding
  ones widen toward the root one component at a time
  ("Project: work/app" vs "Project: personal/app"), widening further if the
  parent also collides. The common no-collision case is unchanged.
- Full path goes in the combo item's ToolTipRole, so a profile is always
  verifiable by hover regardless of label.
- Tightened the "is this a project config?" check: the docstring claimed a
  top-level object but the code only checked is_file(), so a .mcp.json that
  was a JSON array or garbage still became a profile and only failed on
  open. It now must parse (strict) to a dict, else it's skipped.

Tests: +6 (no-collision basename, colliding widen, deeper widen, and
discover_project_configs disambiguation + skipping array/garbage/missing).
463 passed, ruff clean.

Closes #74

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKwBecy6N83jnqQmw8ezwE
the_og merged commit 072a5cdc08 into main 2026-08-03 23:42:19 -04:00
the_og deleted branch fix/74-project-label-collision 2026-08-03 23:42:19 -04:00
Sign in to join this conversation.