feat(agent-memory): add PR-to-branch mapping module#1210
Conversation
Implement Python module for maintaining explicit PR-to-branch mappings in Serena memory. Prevents cross-PR commits during multi-PR sessions. Provides CLI and library interface with add, query, validate, list, and cleanup operations. Stores data as JSON in `.serena/memories/pr-branch-mapping.md`. Includes 33 tests covering all operations and edge cases. Fixes #683 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
✅ Pass: Memory ValidationNo memories with citations found. 📊 Validation Details
|
PR Validation ReportCaution ❌ Status: FAIL Description Validation
QA Validation
|
There was a problem hiding this comment.
Code Review
The pull request introduces a robust module for managing PR-to-branch mappings, which is a critical step in preventing cross-PR commit errors. The implementation is well-structured, leveraging Python dataclasses for data modeling and adhering to project standards for exit codes and subprocess handling. However, a logic flaw was identified in the add_mapping function where multiple PRs could be mapped to the same branch, potentially leading to incorrect validation results. Addressing this will ensure the tool effectively meets its goal of maintaining explicit and unique mappings.
|
Caution Review failedFailed to post review comments 📝 WalkthroughWalkthroughImplements a new CLI-backed Python module that persists PR-to-branch mappings in a Serena memory markdown file and provides add/query/validate/list/cleanup operations; adds comprehensive pytest coverage for persistence, CLI flows, JSON handling, and git-branch interactions. (49 words) Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI
participant Module as PRBranchMapping
participant Git as Git (git)
participant FS as FileSystem (.serena/memories)
CLI->>Module: add(prNumber, branchName, sessionId)
Module->>Git: git branch --show-current
Git-->>Module: branch name / error
Module->>FS: read `pr-branch-mapping.md`
FS-->>Module: markdown (may contain JSON block)
Module->>Module: parse JSON, merge/update mapping, set current_session
Module->>FS: write markdown with embedded JSON
Module-->>CLI: exit 0
CLI->>Module: validate()
Module->>FS: read memory file
FS-->>Module: markdown
Module->>Git: git branch --show-current
Git-->>Module: branch name / error
Module-->>CLI: report consistency / exit code
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (7)
tests/test_pr_branch_mapping.py (3)
226-231: Unused variablemsg.Ruff RUF059 flags unpacked variable
msgas unused. Prefix with underscore.Proposed fix
- is_ok, msg = validate_branch_pr_consistency( + is_ok, _msg = validate_branch_pr_consistency( sample_mapping, current_branch="feat/add-widget" )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_pr_branch_mapping.py` around lines 226 - 231, The test unpacks validate_branch_pr_consistency into is_ok, msg but never uses msg which triggers RUF059; change the unpack to use a prefixed-underscore name (e.g., is_ok, _msg = validate_branch_pr_consistency(...) or is_ok, _ = validate_branch_pr_consistency(...)) in tests/test_pr_branch_mapping.py where validate_branch_pr_consistency is called, and ensure no subsequent references to msg remain.
334-345: Unusedcapsysparameter.Ruff ARG002 flags
capsysas unused. Either remove it or use it to verify stdout content.Option A: Remove unused parameter
- def test_query_by_pr(self, tmp_path: Path, capsys: object) -> None: + def test_query_by_pr(self, tmp_path: Path) -> None:Option B: Actually verify output
- def test_query_by_pr(self, tmp_path: Path, capsys: object) -> None: + def test_query_by_pr(self, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: mapping = PRBranchMapping() add_mapping(mapping, 42, "feat/test") save_mapping(tmp_path, mapping) exit_code = main([ "--project-root", str(tmp_path), "query", "--pr", "42", ]) assert exit_code == 0 + captured = capsys.readouterr() + assert "feat/test" in captured.out🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_pr_branch_mapping.py` around lines 334 - 345, The test function test_query_by_pr currently declares an unused capsys parameter causing Ruff ARG002; either remove the capsys parameter from test_query_by_pr (and leave the rest as-is using PRBranchMapping, add_mapping, save_mapping and main) or, if you intend to validate CLI output, use capsys to capture stdout after calling main (e.g., call capsys.readouterr() and assert on its .out) to verify the expected output for the "query --pr 42" invocation; update assertions accordingly and keep the rest of the setup (PRBranchMapping, add_mapping, save_mapping) intact.
221-221: Repeated import inside test methods.
CurrentSessionis imported inside 4 test methods (lines 221, 236, 253, 310). Move to module-level imports at line 15-27 for consistency.Proposed fix at module level
from scripts.pr_branch_mapping import ( + CurrentSession, MEMORY_RELATIVE_PATH, PRBranchEntry, PRBranchMapping,Then remove the
from scripts.pr_branch_mapping import CurrentSessionlines from inside test methods.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_pr_branch_mapping.py` at line 221, Multiple tests import CurrentSession inside their bodies; move the import to the module-level with the other imports at the top of the test file and remove the four in-function imports. Specifically, add "from scripts.pr_branch_mapping import CurrentSession" to the top import block (with the other test imports) and delete the in-test occurrences of that import in the methods that currently re-import CurrentSession.scripts/pr_branch_mapping.py (4)
27-28: Direct file I/O bypasses MemoriesManager.Per external tools context, repository docs describe MemoriesManager as the canonical API for project-local memories under
.serena/memories/. Direct writes bypass name normalization, file-size limits, and audit semantics.Consider refactoring to use MemoriesManager in a follow-up if consistency with other memory tools is required.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/pr_branch_mapping.py` around lines 27 - 28, The code creates MEMORY_FILENAME and MEMORY_RELATIVE_PATH constants that point directly at ".serena/memories/...", which bypasses the project's MemoriesManager; replace direct file path usage by obtaining the canonical memory path or handle from the MemoriesManager API (e.g., call MemoriesManager.get_memory_path(name) or MemoriesManager.open/create with MEMORY_FILENAME) so name normalization, size limits and audit semantics are applied instead of hardcoding MEMORY_RELATIVE_PATH; update any code that referenced MEMORY_RELATIVE_PATH to use the MemoriesManager methods (retain MEMORY_FILENAME only as a logical name if needed).
87-89: Simplify withdict.get().Ruff RUF019 flags the redundant key check.
dict.get()handles missing keys cleanly.Proposed fix
current = None - if "current_session" in data and data["current_session"]: - current = CurrentSession(**data["current_session"]) + session_data = data.get("current_session") + if session_data: + current = CurrentSession(**session_data)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/pr_branch_mapping.py` around lines 87 - 89, The code redundantly checks "current_session" in data before instantiating CurrentSession; replace that pattern with dict.get() to satisfy Ruff RUF019 by retrieving the value via data.get("current_session") and only instantiate CurrentSession(**...) when the returned value is truthy (e.g., use a conditional expression or an if on value) so the variable current is None when there is no session and becomes CurrentSession(**value) when present.
385-387: Blind exception catch loses stack trace.Ruff BLE001 flags this. For a CLI tool, catching all exceptions is acceptable to prevent crashes. But the current message swallows the traceback, making debugging harder.
Consider logging the exception type or using
logging.exception()when verbose mode is added.Proposed fix
except Exception as exc: - print(f"FATAL: {exc}", file=sys.stderr) + print(f"FATAL: {type(exc).__name__}: {exc}", file=sys.stderr) return 2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/pr_branch_mapping.py` around lines 385 - 387, The broad except block that prints only the exception message (in the except Exception as exc: ... return 2) loses the traceback; update that handler to emit the full traceback and exception type so failures are debuggable (e.g., use logging.exception(...) or traceback.print_exc(file=sys.stderr) instead of print(f"FATAL: {exc}", ...)). Locate the top-level exception handler in the script (the except Exception as exc: block that returns 2) and replace the single-line print with a call that logs the traceback and message; if a verbose/logging mode exists or is added, prefer logging.exception for consistent logging.
109-148: Function mutates input and returns it.
add_mappingmodifiesmappingin place and also returns it. This dual pattern can confuse callers about ownership—some may expect a pure function returning a new object, others may expect a void mutator.Current usage appears consistent, but consider documenting this explicitly in the docstring or picking one pattern.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/pr_branch_mapping.py` around lines 109 - 148, The add_mapping function mutates the provided PRBranchMapping in place and also returns it, which can confuse callers; update the add_mapping docstring to explicitly state that it modifies the supplied mapping object (PRBranchMapping) in-place and returns the same modified instance, and reference the behavior of fields updated/added (mapping.mappings appends PRBranchEntry, existing entry fields branch_name/last_session are updated, and mapping.current_session is replaced with a CurrentSession) so callers know it is not a pure copy-producing function (alternatively, if you prefer an immutable API, replace in-place updates in add_mapping/_find_entry with code that clones mapping and returns a new PRBranchMapping — choose one approach and make docstring/signature consistent).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@scripts/pr_branch_mapping.py`:
- Around line 27-28: The code creates MEMORY_FILENAME and MEMORY_RELATIVE_PATH
constants that point directly at ".serena/memories/...", which bypasses the
project's MemoriesManager; replace direct file path usage by obtaining the
canonical memory path or handle from the MemoriesManager API (e.g., call
MemoriesManager.get_memory_path(name) or MemoriesManager.open/create with
MEMORY_FILENAME) so name normalization, size limits and audit semantics are
applied instead of hardcoding MEMORY_RELATIVE_PATH; update any code that
referenced MEMORY_RELATIVE_PATH to use the MemoriesManager methods (retain
MEMORY_FILENAME only as a logical name if needed).
- Around line 87-89: The code redundantly checks "current_session" in data
before instantiating CurrentSession; replace that pattern with dict.get() to
satisfy Ruff RUF019 by retrieving the value via data.get("current_session") and
only instantiate CurrentSession(**...) when the returned value is truthy (e.g.,
use a conditional expression or an if on value) so the variable current is None
when there is no session and becomes CurrentSession(**value) when present.
- Around line 385-387: The broad except block that prints only the exception
message (in the except Exception as exc: ... return 2) loses the traceback;
update that handler to emit the full traceback and exception type so failures
are debuggable (e.g., use logging.exception(...) or
traceback.print_exc(file=sys.stderr) instead of print(f"FATAL: {exc}", ...)).
Locate the top-level exception handler in the script (the except Exception as
exc: block that returns 2) and replace the single-line print with a call that
logs the traceback and message; if a verbose/logging mode exists or is added,
prefer logging.exception for consistent logging.
- Around line 109-148: The add_mapping function mutates the provided
PRBranchMapping in place and also returns it, which can confuse callers; update
the add_mapping docstring to explicitly state that it modifies the supplied
mapping object (PRBranchMapping) in-place and returns the same modified
instance, and reference the behavior of fields updated/added (mapping.mappings
appends PRBranchEntry, existing entry fields branch_name/last_session are
updated, and mapping.current_session is replaced with a CurrentSession) so
callers know it is not a pure copy-producing function (alternatively, if you
prefer an immutable API, replace in-place updates in add_mapping/_find_entry
with code that clones mapping and returns a new PRBranchMapping — choose one
approach and make docstring/signature consistent).
In `@tests/test_pr_branch_mapping.py`:
- Around line 226-231: The test unpacks validate_branch_pr_consistency into
is_ok, msg but never uses msg which triggers RUF059; change the unpack to use a
prefixed-underscore name (e.g., is_ok, _msg =
validate_branch_pr_consistency(...) or is_ok, _ =
validate_branch_pr_consistency(...)) in tests/test_pr_branch_mapping.py where
validate_branch_pr_consistency is called, and ensure no subsequent references to
msg remain.
- Around line 334-345: The test function test_query_by_pr currently declares an
unused capsys parameter causing Ruff ARG002; either remove the capsys parameter
from test_query_by_pr (and leave the rest as-is using PRBranchMapping,
add_mapping, save_mapping and main) or, if you intend to validate CLI output,
use capsys to capture stdout after calling main (e.g., call capsys.readouterr()
and assert on its .out) to verify the expected output for the "query --pr 42"
invocation; update assertions accordingly and keep the rest of the setup
(PRBranchMapping, add_mapping, save_mapping) intact.
- Line 221: Multiple tests import CurrentSession inside their bodies; move the
import to the module-level with the other imports at the top of the test file
and remove the four in-function imports. Specifically, add "from
scripts.pr_branch_mapping import CurrentSession" to the top import block (with
the other test imports) and delete the in-test occurrences of that import in the
methods that currently re-import CurrentSession.
Address Gemini code review comment that add_mapping allowed duplicate branch mappings across different PRs. Now removes any existing mapping for a branch before adding a new one, ensuring 1:1 PR-to-branch relationship. Added two tests to verify: - Old PR mapping is removed when new PR claims same branch - Other PR mappings with different branches are preserved Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AI Quality Gate ReviewWarning WalkthroughThis PR was reviewed by six AI agents in parallel, analyzing different aspects of the changes:
Review Summary
💡 Quick Access: Click on individual agent jobs (e.g., "🔒 security Review", "🧪 qa Review") in the workflow run to see detailed findings and step summaries. Security Review DetailsVERDICT: CRITICAL_FAIL QA Review DetailsVERDICT: CRITICAL_FAIL Analyst Review DetailsVERDICT: CRITICAL_FAIL Architect Review DetailsVERDICT: CRITICAL_FAIL DevOps Review DetailsVERDICT: CRITICAL_FAIL Roadmap Review DetailsVERDICT: CRITICAL_FAIL Run Details
Powered by AI Quality Gate workflow |
Pull Request
Summary
Add Python module for maintaining explicit PR-to-branch mappings in Serena memory. This prevents cross-PR commits during multi-PR sessions, addressing the root cause identified in the PR #669 retrospective.
Specification References
Changes
scripts/pr_branch_mapping.pywith CLI and library interface for PR-branch mapping operationstests/test_pr_branch_mapping.pywith 33 tests covering all operations and edge casesPRBranchEntry,CurrentSession,PRBranchMapping)Type of Change
Testing
Agent Review
Security Review
Other Agent Reviews
Checklist
Related Issues