Skip to content

feat: Add project aliases for renamed project matching#102

Merged
evansenter merged 4 commits into
mainfrom
feat/project-aliases-issue-71
Feb 7, 2026
Merged

feat: Add project aliases for renamed project matching#102
evansenter merged 4 commits into
mainfrom
feat/project-aliases-issue-71

Conversation

@evansenter

Copy link
Copy Markdown
Owner

Summary

  • Adds project_aliases table for mapping alias names to historical project paths
  • Modifies build_where_clause() to expand aliases to OR clauses in all project-filtered queries
  • Case-insensitive matching via COLLATE NOCASE

Example: When a project is renamed from rust-genai to genai-rs:

agent-session-analytics-cli alias add genai-rs rust-genai

Now --project genai-rs matches both "genai-rs" AND "rust-genai" in all queries.

Test plan

  • Run make check (410 tests pass)
  • Verify alias CRUD operations work via CLI
  • Verify query expansion matches both alias and target patterns
  • Verify case-insensitive matching

Fixes #71

🤖 Generated with Claude Code

When projects are renamed, historical data stored under the old name
doesn't match new filters. This adds a project_aliases table and
modifies build_where_clause() to automatically expand aliases to OR
clauses, making `--project genai-rs` match both "genai-rs" AND
"rust-genai".

Changes:
- Add project_aliases table with COLLATE NOCASE for case-insensitive matching
- Add storage methods: add/remove/get/resolve project aliases
- Modify build_where_clause() to expand aliases in all project-filtered queries
- Add MCP tools: add_project_alias, remove_project_alias, list_project_aliases
- Add CLI subcommands: alias add/remove/list
- Add 15 tests for alias functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude

claude Bot commented Jan 31, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds a project alias feature that allows mapping old project names to new ones (e.g., after a repo rename), so queries can match historical data stored under different project paths. It includes storage layer, MCP tools, CLI commands, migration, and comprehensive tests.

Issues Found

Critical

None

Important

  • queries.py:1127-1129 - The classify_sessions function manually builds a project filter (where_parts.append("project_path LIKE ?")) without using build_where_clause(), so it doesn't get alias expansion. Users filtering by alias will get incomplete results from session classification. Should use build_where_clause(cutoff=cutoff, project=project, storage=storage) for consistency.

Suggestions

  • queries.py:354-381 - The query_timeline function passes project_path=project to storage.get_events_in_range(), which does exact matching (project_path = ?), not LIKE matching. This is different behavior from other project-filtered queries. Consider whether this function should also support aliases (would require modifying the storage method or rewriting the query to use build_where_clause).

Verdict

REQUEST_CHANGES - One query function bypasses the alias expansion logic, creating inconsistent behavior.


Automated review by Claude Code

…ansion

Address reviewer feedback: classify_sessions was manually building
project filter without using build_where_clause(), bypassing alias
expansion logic.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@evansenter

Copy link
Copy Markdown
Owner Author

Feedback Addressed

Implemented

  • [Important] queries.py:1127-1129 - Fixed classify_sessions to use build_where_clause() for alias expansion. Was manually building project filter and bypassing alias logic.

Skipped

  • [Suggestion] queries.py:354-381 - query_timeline uses storage.get_events_in_range() which has different semantics (exact matching). This is a broader change outside the scope of this PR.

@claude

claude Bot commented Feb 6, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds a project_aliases table and supporting infrastructure (storage methods, MCP tools, CLI subcommands, migration, tests) to allow renamed projects to match historical data under their old names. The build_where_clause() helper is extended with an optional storage parameter for alias resolution, and all existing callers are updated.

Issues Found

Critical

None

Important

  • storage.py:1765 / server.py:419-423 / cli.py:1770-1776 - Dead code: add_project_alias() uses INSERT OR IGNORE which silently swallows the UNIQUE(alias, target) constraint violation. The except sqlite3.IntegrityError handlers in both the MCP tool and CLI command will never execute. Either remove the dead exception handlers (and accept silent dedup), or change the SQL to plain INSERT INTO so the IntegrityError is actually raised and the "Alias already exists" feedback path works. The current code misleads maintainers into thinking duplicates are detected and reported.

Suggestions

  • cli.py:1764-1777 - Because INSERT OR IGNORE silently succeeds on duplicates, cmd_alias_add always reports {"status": "ok", "alias": ..., "target": ...} whether the alias was newly created or already existed. Consider having add_project_alias() return a boolean (e.g., by checking cursor.rowcount) so callers can give accurate feedback to users about whether the alias was actually added.

Previously Addressed (Filtered)

  • queries.py:1127-1129 - classify_sessions bypassing alias expansion (Implemented in prior round)
  • queries.py:354-381 - query_timeline uses different matching semantics (Skipped: out of scope)

2 items from prior feedback rounds were not re-raised.

Verdict

REQUEST_CHANGES - The IntegrityError exception handling is dead code due to INSERT OR IGNORE, creating an inconsistency between the storage layer contract and its callers.


Automated review by Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude

claude Bot commented Feb 6, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds a project_aliases table with CRUD operations (storage, MCP tools, CLI subcommands), migration v14, and extends build_where_clause() with an optional storage parameter to automatically expand aliases into OR clauses for all project-filtered queries. Comprehensive tests (15 storage + 2 query-level) cover the feature.

Issues Found

Critical

None

Important

  • storage.py:1765 / server.py:194 / cli.py:1776 - Dead code from prior review round, still unresolved: add_project_alias() uses INSERT OR IGNORE which silently swallows the UNIQUE(alias, target) constraint violation. The except sqlite3.IntegrityError handlers in both server.py:191-195 and cli.py:1773-1782 will never execute. Either remove the dead exception handlers (accepting silent dedup), or change the SQL to plain INSERT INTO so the IntegrityError is actually raised and the "Alias already exists" feedback path works. The current code misleads maintainers into thinking duplicates are detected and reported.

Suggestions

  • cli.py:1770-1783 - Also from prior review round, still unresolved: Because INSERT OR IGNORE silently succeeds on duplicates, cmd_alias_add always reports {"status": "ok", "alias": ..., "target": ...} whether the alias was newly created or already existed. Consider having add_project_alias() return a boolean (e.g., by checking cursor.rowcount) so callers can give accurate feedback about whether the alias was actually added vs. already existed.

Previously Addressed (Filtered)

  • queries.py:1127-1129 - classify_sessions bypassing alias expansion (Implemented in prior round)
  • queries.py:354-381 - query_timeline uses different matching semantics (Skipped: out of scope)

2 items from prior feedback rounds were not re-raised.

Verdict

REQUEST_CHANGES - The IntegrityError exception handling remains dead code due to INSERT OR IGNORE. This was raised in the previous review round but has not been addressed or responded to in a Feedback Addressed comment.


Automated review by Claude Code

@claude

claude Bot commented Feb 7, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds a project_aliases table with CRUD operations (storage, MCP tools, CLI subcommands), migration v14, and extends build_where_clause() with an optional storage parameter to automatically expand aliases into OR clauses for all project-filtered queries. Includes 15 storage tests and 2 query-level integration tests.

Issues Found

Critical

None

Important

  • storage.py:1765 / server.py:224-227 / cli.py:1773-1781 - Still unresolved from prior review rounds: add_project_alias() uses INSERT OR IGNORE which silently swallows the UNIQUE(alias, target) constraint violation. The except sqlite3.IntegrityError handlers in both server.py and cli.py will never execute — they are dead code. Either (a) remove the dead exception handlers and accept silent dedup, or (b) change the SQL to plain INSERT INTO so the IntegrityError is actually raised and the "Alias already exists" feedback path works. The current code misleads maintainers into thinking duplicates are detected and reported to users.

Suggestions

  • cli.py:1770-1783 / server.py:222-227 - Also still unresolved: Because INSERT OR IGNORE silently succeeds on duplicates, callers always report {"status": "ok"} whether the alias was newly created or already existed. Consider having add_project_alias() return a boolean (e.g., by checking cursor.rowcount) so callers can give accurate user-facing feedback ("added" vs "already exists").

Previously Addressed (Filtered)

  • queries.py:1127-1129 - classify_sessions bypassing alias expansion (Implemented in prior round)
  • queries.py:354-381 - query_timeline uses different matching semantics (Skipped: out of scope)

2 items from prior feedback rounds were not re-raised.

Verdict

REQUEST_CHANGES - The IntegrityError exception handling is dead code due to INSERT OR IGNORE. This was raised in two prior review rounds but has not been addressed or responded to in a Feedback Addressed comment.


Automated review by Claude Code

@evansenter evansenter merged commit f5625d6 into main Feb 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project path filtering uses literal match - renamed projects don't match

1 participant