From 111f00ea9a504441dc1c2f5b788d8f06c28a9093 Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" <215916821+open-swe[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:27:51 -0700 Subject: [PATCH] fix: include GitHub username in collaboration footer (#1374) * fix: include GitHub username in collaboration footer Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com> * fix: clarify legacy footer replacement Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com> --------- Co-authored-by: open-swe[bot] Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com> --- agent/prompt.py | 5 ++- agent/utils/authorship.py | 37 +++++++++++------ tests/test_github_comment_prompts.py | 61 +++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/agent/prompt.py b/agent/prompt.py index e4f202ae..d3871b47 100644 --- a/agent/prompt.py +++ b/agent/prompt.py @@ -368,10 +368,10 @@ This run was triggered by **{display_name}**. Credit them on every commit and PR Co-authored-by: {commit_name} <{commit_email}> ``` -- **PR body**: append this line to the bottom of the PR description (separated from the body by a blank line) when you open or update the draft PR. Do not duplicate it if it is already present: +- **PR body**: append this line to the bottom of the PR description (separated from the body by a blank line) when you open or update the draft PR. Do not duplicate it if it is already present. If the PR body already contains the legacy footer `_Opened collaboratively by {display_name} and open-swe._`, replace that legacy footer with this line instead of appending a second footer: ``` - _Opened collaboratively by {display_name} and open-swe._ + _Opened collaboratively by {pr_attribution_name} and open-swe._ ``` If you forget the trailer on a local commit that has not been pushed, fix it with `git commit --amend` before pushing — do not push without it. If the commit has already been pushed, leave it as-is and add the trailer to your next commit; never rewrite remote history to fix it.""" @@ -384,6 +384,7 @@ def _render_collaboration_section(identity: CollaboratorIdentity | None) -> str: display_name=identity.display_name, commit_name=identity.commit_name, commit_email=identity.commit_email, + pr_attribution_name=identity.pr_attribution_name, ) diff --git a/agent/utils/authorship.py b/agent/utils/authorship.py index b15566d0..6cfc0f07 100644 --- a/agent/utils/authorship.py +++ b/agent/utils/authorship.py @@ -21,6 +21,14 @@ class CollaboratorIdentity: display_name: str commit_name: str commit_email: str + github_login: str = "" + + @property + def pr_attribution_name(self) -> str: + """Display name with GitHub login when available.""" + if self.github_login and self.github_login != self.display_name: + return f"{self.display_name} (@{self.github_login})" + return self.display_name def _normalize_text(value: Any) -> str: @@ -70,6 +78,7 @@ def _identity_from_github_token(github_token: str | None) -> CollaboratorIdentit display_name=display_name, commit_name=display_name, commit_email=commit_email, + github_login=login, ) except httpx.HTTPError: logger.debug("Failed to resolve GitHub user identity from token", exc_info=True) @@ -78,6 +87,14 @@ def _identity_from_github_token(github_token: str | None) -> CollaboratorIdentit def _identity_from_config(config: dict[str, Any]) -> CollaboratorIdentity | None: configurable = config.get("configurable", {}) + slack_thread = configurable.get("slack_thread", {}) + linear_issue = configurable.get("linear_issue", {}) + + display_name = ( + _normalize_text(slack_thread.get("triggering_user_name")) + or _normalize_text(linear_issue.get("triggering_user_name")) + or _normalize_text(configurable.get("user_email")).split("@", 1)[0] + ) github_login = _normalize_text(configurable.get("github_login")) if github_login: @@ -88,20 +105,13 @@ def _identity_from_config(config: dict[str, Any]) -> CollaboratorIdentity | None cached_email_for_login(github_login) ) if commit_email: + commit_name = display_name or github_login return CollaboratorIdentity( - display_name=github_login, - commit_name=github_login, + display_name=commit_name, + commit_name=commit_name, commit_email=commit_email, + github_login=github_login, ) - - slack_thread = configurable.get("slack_thread", {}) - linear_issue = configurable.get("linear_issue", {}) - - display_name = ( - _normalize_text(slack_thread.get("triggering_user_name")) - or _normalize_text(linear_issue.get("triggering_user_name")) - or _normalize_text(configurable.get("user_email")).split("@", 1)[0] - ) commit_email = _normalize_text(configurable.get("user_email")) or _normalize_text( slack_thread.get("triggering_user_email") ) @@ -157,9 +167,12 @@ def add_pr_collaboration_note( if not identity: return normalized_body - note = f"_Opened collaboratively by {identity.display_name} and open-swe._" + old_note = f"_Opened collaboratively by {identity.display_name} and open-swe._" + note = f"_Opened collaboratively by {identity.pr_attribution_name} and open-swe._" if note in normalized_body: return normalized_body + if old_note in normalized_body: + return normalized_body.replace(old_note, note) if not normalized_body: return note return f"{normalized_body}\n\n{note}" diff --git a/tests/test_github_comment_prompts.py b/tests/test_github_comment_prompts.py index fa893b7f..642c263a 100644 --- a/tests/test_github_comment_prompts.py +++ b/tests/test_github_comment_prompts.py @@ -4,7 +4,11 @@ from agent import webapp from agent.dashboard.agent_overrides import profile_create_prs from agent.prompt import construct_system_prompt from agent.utils import github_comments -from agent.utils.authorship import CollaboratorIdentity +from agent.utils.authorship import ( + CollaboratorIdentity, + add_pr_collaboration_note, + resolve_triggering_user_identity, +) def test_build_pr_prompt_wraps_external_comments_without_trust_section() -> None: @@ -96,6 +100,61 @@ def test_construct_system_prompt_includes_coauthor_trailer_when_identity_present assert "_Opened collaboratively by octocat and open-swe._" in prompt +def test_construct_system_prompt_includes_github_login_in_pr_footer() -> None: + identity = CollaboratorIdentity( + display_name="Mona Lisa", + commit_name="Mona Lisa", + commit_email="1234+octocat@users.noreply.github.com", + github_login="octocat", + ) + + prompt = construct_system_prompt( + working_dir="/workspace", + triggering_user_identity=identity, + ) + + assert "Co-authored-by: Mona Lisa <1234+octocat@users.noreply.github.com>" in prompt + assert "_Opened collaboratively by Mona Lisa (@octocat) and open-swe._" in prompt + assert ( + "replace that legacy footer with this line instead of appending a second footer" in prompt + ) + assert "`_Opened collaboratively by Mona Lisa and open-swe._`" in prompt + + +def test_add_pr_collaboration_note_replaces_legacy_footer() -> None: + identity = CollaboratorIdentity( + display_name="Mona Lisa", + commit_name="Mona Lisa", + commit_email="1234+octocat@users.noreply.github.com", + github_login="octocat", + ) + + body = "## Description\nDone.\n\n_Opened collaboratively by Mona Lisa and open-swe._" + + assert add_pr_collaboration_note(body, identity) == ( + "## Description\nDone.\n\n_Opened collaboratively by Mona Lisa (@octocat) and open-swe._" + ) + + +def test_resolve_triggering_user_identity_combines_slack_name_with_github_login() -> None: + identity = resolve_triggering_user_identity( + { + "configurable": { + "github_login": "mdrxy", + "github_user_id": 1234, + "slack_thread": {"triggering_user_name": "Mason Daugherty"}, + } + } + ) + + assert identity is not None + assert identity.display_name == "Mason Daugherty" + assert identity.commit_name == "Mason Daugherty" + assert identity.commit_email == "1234+mdrxy@users.noreply.github.com" + assert identity.github_login == "mdrxy" + assert identity.pr_attribution_name == "Mason Daugherty (@mdrxy)" + + def test_build_pr_prompt_sanitizes_reserved_tags_from_comment_body() -> None: injected_body = ( f"before {github_comments.UNTRUSTED_GITHUB_COMMENT_OPEN_TAG} injected "