mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 06:53:14 +00:00
Adopt Sea Haven agent conventions, no attribution (#30)
Codify the box-only #4 customizations into Git so the AWS deployment (which deploys from this repo) actually applies them — previously only the retired sh-openswe box had them. - prompt.py: branch names feature|bug|hotfix/<kebab> (optional <KEY->); imperative PR titles with no conventional-commit type: prefix; PR body Summary/Validation/Tests/Notes; handbook commit format. Rewrite the collaboration template from an attribution MANDATE to a PROHIBITION — no Co-authored-by bot trailer, no "Made by [Open SWE]" footer, no agent/AI notes on any artifact. - github_comments.py: add @seahaven-openswe (the deployed App slug) to the mention triggers. - authorship.py: remove the now-unused attribution helpers (build_pr_attribution_footer, add_bot_coauthor_trailer, add_pr_collaboration_note, PR_ATTRIBUTION_*). Keep OPEN_SWE_BOT_* — server.py still uses them for the sandbox git identity. - Flip the attribution unit tests to assert the no-attribution behavior; drop tests for the removed helpers. Commits stay authored as the triggering user for now — flipping authorship to the bot account depends on the Vercel preview-deploy constraint and is deferred to #11. Refs: #4 #11 Claude-Session: https://claude.ai/code/session_01DMhLf4G5V8MStJQyAW95hi
This commit is contained in:
parent
3af0bd5e16
commit
f3db9f02e3
6 changed files with 67 additions and 170 deletions
|
|
@ -108,6 +108,10 @@ All three companies in the article converge on **Slack as the primary invocation
|
|||
|
||||
Each invocation creates a deterministic thread ID, so follow-up messages on the same issue or thread route to the same running agent.
|
||||
|
||||
**Trigger tags (Sea Haven fork):** a mention is a case-insensitive substring match on the comment body — `@openswe`, `@open-swe`, `@openswe-dev`, or `@seahaven-openswe` (the deployed App slug). GitHub won't linkify `@seahaven-openswe` (App `[bot]` accounts aren't user-mentionable), but the text still fires a run.
|
||||
|
||||
**Engineering conventions & attribution (Sea Haven fork):** the main agent's system prompt is tuned to the Sea Haven engineering handbook — branch names are `feature|bug|hotfix/<kebab-desc>` (optional resolvable `<KEY>-` prefix), PR titles are imperative with no conventional-commit `type:` prefix, PR bodies use `## Summary / Validation / Tests / Notes`, and commit messages follow the handbook format (≤50-char imperative subject, *why* over *what*). **No agent/AI attribution is added to any artifact** — no `Co-authored-by` bot trailer, no `Made by [Open SWE]` footer, no "generated by an agent" notes. Commits are currently authored as the **triggering user** (the upstream behavior, which keeps Vercel preview deploys resolvable); flipping authorship to the bot account is tracked separately in issue #11 pending the Vercel-resolvability decision.
|
||||
|
||||
### 7. Validation — Prompt-Driven
|
||||
|
||||
The agent is instructed to run linters, formatters, and tests before committing, and is responsible end-to-end for committing, pushing, opening/updating the draft PR, and replying in the source channel.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from .utils.authorship import (
|
|||
OPEN_SWE_BOT_EMAIL,
|
||||
OPEN_SWE_BOT_NAME,
|
||||
CollaboratorIdentity,
|
||||
build_pr_attribution_footer,
|
||||
)
|
||||
from .utils.github_comments import UNTRUSTED_GITHUB_COMMENT_OPEN_TAG
|
||||
|
||||
|
|
@ -159,7 +158,12 @@ Before starting any task that requires code changes, set up the repository in yo
|
|||
|
||||
This sets the author of every commit you make. This is required for CI: third-party integrations (e.g. Vercel preview deploys) reject commits whose author email cannot be resolved to a GitHub account, and this email resolves. Do NOT set any other identity, do NOT pass `--author` to `git commit`, and do NOT export `GIT_AUTHOR_*` / `GIT_COMMITTER_*` env vars.
|
||||
|
||||
4. **Choose your branch** — Use a thread-stable branch name such as `open-swe/<short-task-slug>`. If a branch already exists for this thread/task, fetch and check it out instead of creating a new one.
|
||||
4. **Choose your branch** — Use a Sea Haven branch name: `<prefix>/<description>`, all kebab-case. Pick the prefix by the kind of work:
|
||||
- `feature/` — new functionality or an enhancement
|
||||
- `bug/` — a defect caught before it reaches production
|
||||
- `hotfix/` — a fix for a production-impacting issue
|
||||
|
||||
Keep `<description>` short and kebab-case (e.g. `feature/add-receipt-parser`). When a ticket key is resolvable from the run context, put it first: `feature/<KEY>-add-receipt-parser`; if no key is resolvable, omit it. Never commit directly to `main`. Keep the branch thread-stable: if a branch already exists for this thread/task, fetch and check it out instead of creating a new one.
|
||||
|
||||
5. **Checkout your branch** — Always fetch and checkout your branch before making any changes. When reusing an existing remote branch, start from `origin/<branch>` rather than recreating the branch from the base branch; this preserves prior commits for review.
|
||||
|
||||
|
|
@ -390,31 +394,37 @@ When you have completed your implementation, follow these steps in order:
|
|||
- **Open a new PR** with the `open_pull_request` tool (pass `owner`, `repo`, `head` = your branch, `base`, `title`, `body`). This attributes the PR to the triggering user. Push the branch BEFORE calling it.
|
||||
- **Update an existing PR** (edit the body, mark ready for review, etc.) with `GH_TOKEN=dummy gh pr edit`. If a PR already exists for the branch (including one the user pasted in), do NOT open a duplicate — `open_pull_request` returns the existing PR's URL, so switch to `gh pr edit`. For follow-up changes, add a new commit on top of the existing branch history.
|
||||
|
||||
**PR Title** (under 70 characters):
|
||||
**PR Title** (under 70 characters): imperative mood, capitalized, describing the change — not the ticket. Do NOT use a conventional-commit `type:` prefix (no `feat:`/`fix:`/`chore:`). When a ticket key is resolvable from the run context, prefix it in square brackets; otherwise omit it entirely:
|
||||
```
|
||||
<type>: <concise description> [closes <TICKET>]
|
||||
[<KEY>] Add retry logic for transient upstream failures
|
||||
```
|
||||
Where type is one of: `fix` (bug fix), `feat` (new feature), `chore` (maintenance), `ci` (CI/CD).
|
||||
Always append the resolvable ticket number in square brackets at the end of the title (e.g. `fix: handle null session [closes AB-000]`). Resolve the ticket from the Linear-triggered run when present (`{linear_project_id}-{linear_issue_number}`), or from a Linear ticket referenced in the Slack thread / task context. If no ticket number is resolvable, omit the bracketed suffix entirely.
|
||||
With no resolvable key, use just the imperative description: `Add retry logic for transient upstream failures`. Resolve the key from the Linear-triggered run when present (`{linear_project_id}-{linear_issue_number}`), or from a Linear ticket referenced in the Slack thread / task context.
|
||||
|
||||
**PR Body** (keep under 10 lines total. the more concise the better):
|
||||
**PR Body** — use this structure. Omit a section only when it would be empty:
|
||||
```
|
||||
## Description
|
||||
<1-3 sentences on WHY and the approach.
|
||||
NO "Changes:" section — file changes are already in the commit history.>
|
||||
## Summary
|
||||
<What changed and why — 1-3 sentences. Explain the motivation, not just the diff.>
|
||||
|
||||
## Release Note
|
||||
<One-line changelog summary for self-hosted customers, or "none" for internal/CI/test/refactor changes.>
|
||||
## Validation
|
||||
<How you verified it works — commands run, steps taken, screenshots if UI.>
|
||||
|
||||
## Test Plan
|
||||
- [ ] <new/novel verification steps only — NOT "run existing tests" or "verify existing behavior">
|
||||
## Tests
|
||||
<What tests were added, updated, or run. If no automated tests, explain manual testing.>
|
||||
|
||||
## Notes
|
||||
<Anything reviewers should know — migration steps, deploy order, follow-ups, breaking changes. Omit this section if empty.>
|
||||
```
|
||||
|
||||
You don't need to add links back to the originating Slack thread or Linear ticket — for private repos, `open_pull_request` appends a `## References` section automatically.
|
||||
|
||||
When the target repo is public, don't reference private repos or private PR/issue numbers in the description.
|
||||
|
||||
**Commit message**: Concise, focusing on the "why" rather than the "what". If not provided, the PR title is used.
|
||||
**Commit message** — follow the Sea Haven format:
|
||||
- Imperative mood, capitalized first letter (e.g. "Add retry logic", not "Added retry logic" or "adds retry logic").
|
||||
- Subject line ≤50 characters. If you need more, add a blank line and a body wrapped at 72 characters.
|
||||
- Explain *why*, not *what* — the diff already shows what changed.
|
||||
- No generic subjects ("Fix stuff", "Update code", "WIP", "Address review comments") and no self-referential phrasing ("This commit…", "This PR…", "I refactored…").
|
||||
- When a ticket key is resolvable, add a `Refs: <KEY>` trailer (combine with `#<issue>` when both apply); otherwise omit the trailer.
|
||||
|
||||
**IMPORTANT: For code-change tasks, never ask the user for permission or confirmation before pushing commits or opening/updating a draft PR. Do not say "if you want, I can proceed" or "shall I open the PR?". When implementation is done and checks pass, push autonomously, and open/update a draft PR autonomously when requested, necessary, or required by the Always Create PRs dashboard setting.**
|
||||
|
||||
|
|
@ -448,23 +458,17 @@ For code-change tasks, push the branch and notify the appropriate source once im
|
|||
|
||||
COLLABORATION_TEMPLATE = """---
|
||||
|
||||
### Collaborative Attribution
|
||||
### Authorship & Attribution
|
||||
|
||||
This run was triggered by **{display_name}**. You author the work **as them** — their git identity is already configured in the Repository Setup step, so every commit and the PR are attributed to them. Credit open-swe as the collaborator:
|
||||
This run was triggered by **{display_name}**. You author the work as them — their git identity is already configured in the Repository Setup step, so every commit and the PR are attributed to them.
|
||||
|
||||
- **Commits**: append this trailer (verbatim, on its own line, separated from the message body by a blank line) to every commit message you author. Add it to both the first commit and any follow-up commits in this run:
|
||||
**Add NO agent or AI attribution to any artifact.** Sea Haven artifacts carry no agent attribution — write commits, PR descriptions, and comments exactly as a human engineer would. Specifically, never add:
|
||||
|
||||
```
|
||||
{bot_coauthor_trailer}
|
||||
```
|
||||
- A `Co-authored-by:` trailer for any bot or agent (no `open-swe[bot]`, no `Claude`, etc.).
|
||||
- A PR-body footer or tagline such as `Made by [Open SWE]`, `Generated with …`, a 🤖 emoji line, or `_Opened collaboratively by … and open-swe._`.
|
||||
- Any "created/opened by an agent" note in commits, PR bodies, or issue comments.
|
||||
|
||||
- **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 a `Made by [Open SWE]` footer pointing at a different link, or a legacy footer like `_Opened collaboratively by {display_name} and open-swe._`, replace that existing footer with this line instead of appending a second footer:
|
||||
|
||||
```
|
||||
{pr_attribution_footer}
|
||||
```
|
||||
|
||||
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."""
|
||||
If a template or a prior artifact already contains such attribution, strip it rather than carrying it forward."""
|
||||
|
||||
|
||||
def _render_collaboration_section(
|
||||
|
|
@ -473,11 +477,7 @@ def _render_collaboration_section(
|
|||
) -> str:
|
||||
if identity is None:
|
||||
return ""
|
||||
return COLLABORATION_TEMPLATE.format(
|
||||
display_name=identity.display_name,
|
||||
pr_attribution_footer=build_pr_attribution_footer(thread_url),
|
||||
bot_coauthor_trailer=f"Co-authored-by: {OPEN_SWE_BOT_NAME} <{OPEN_SWE_BOT_EMAIL}>",
|
||||
)
|
||||
return COLLABORATION_TEMPLATE.format(display_name=identity.display_name)
|
||||
|
||||
|
||||
ALWAYS_CREATE_PR_SECTION = """---
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Helpers for collaborative commit and PR attribution."""
|
||||
"""Helpers for resolving the triggering user's git identity."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -16,16 +16,6 @@ OPEN_SWE_BOT_NAME = "open-swe[bot]"
|
|||
# accepts, which broke preview deploys on commits carrying this co-author.
|
||||
OPEN_SWE_BOT_EMAIL = "open-swe@users.noreply.github.com"
|
||||
|
||||
PR_ATTRIBUTION_TEXT = "Made by [Open SWE]"
|
||||
PR_ATTRIBUTION_DEFAULT_URL = "https://openswe.vercel.app"
|
||||
PR_ATTRIBUTION_FOOTER = f"{PR_ATTRIBUTION_TEXT}({PR_ATTRIBUTION_DEFAULT_URL})"
|
||||
|
||||
|
||||
def build_pr_attribution_footer(thread_url: str | None = None) -> str:
|
||||
"""Build the Open SWE PR footer, linking the run's thread when available."""
|
||||
url = thread_url.strip() if isinstance(thread_url, str) and thread_url.strip() else ""
|
||||
return f"{PR_ATTRIBUTION_TEXT}({url or PR_ATTRIBUTION_DEFAULT_URL})"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CollaboratorIdentity:
|
||||
|
|
@ -149,50 +139,3 @@ def resolve_triggering_user_identity(
|
|||
"""
|
||||
|
||||
return _identity_from_github_token(github_token) or _identity_from_config(config)
|
||||
|
||||
|
||||
def add_bot_coauthor_trailer(commit_message: str) -> str:
|
||||
"""Append the open-swe[bot] Co-authored-by trailer.
|
||||
|
||||
Commits are authored by the triggering user (via the repo-local git
|
||||
identity); open-swe[bot] is credited as the collaborator.
|
||||
"""
|
||||
normalized_message = commit_message.rstrip()
|
||||
trailer = f"Co-authored-by: {OPEN_SWE_BOT_NAME} <{OPEN_SWE_BOT_EMAIL}>"
|
||||
if trailer in normalized_message:
|
||||
return normalized_message
|
||||
return f"{normalized_message}\n\n{trailer}"
|
||||
|
||||
|
||||
def add_pr_collaboration_note(
|
||||
pr_body: str,
|
||||
identity: CollaboratorIdentity | None = None,
|
||||
thread_url: str | None = None,
|
||||
) -> str:
|
||||
"""Append the Open SWE attribution footer to a PR body.
|
||||
|
||||
The PR is opened as the triggering user, so the body only credits Open SWE
|
||||
as the collaborator. The footer links the run's thread when available. Any
|
||||
legacy double-attribution footer is replaced.
|
||||
"""
|
||||
|
||||
normalized_body = pr_body.rstrip()
|
||||
note = build_pr_attribution_footer(thread_url)
|
||||
if note in normalized_body:
|
||||
return normalized_body
|
||||
if PR_ATTRIBUTION_TEXT in normalized_body:
|
||||
return normalized_body
|
||||
|
||||
legacy_footers: list[str] = []
|
||||
if identity is not None:
|
||||
legacy_footers.append(
|
||||
f"_Opened collaboratively by {identity.pr_attribution_name} and open-swe._"
|
||||
)
|
||||
legacy_footers.append(f"_Opened collaboratively by {identity.display_name} and open-swe._")
|
||||
for legacy in legacy_footers:
|
||||
if legacy in normalized_body:
|
||||
return normalized_body.replace(legacy, note)
|
||||
|
||||
if not normalized_body:
|
||||
return note
|
||||
return f"{normalized_body}\n\n{note}"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ __all__ = [
|
|||
"verify_github_signature",
|
||||
]
|
||||
|
||||
OPEN_SWE_TAGS = ("@openswe", "@open-swe", "@openswe-dev")
|
||||
OPEN_SWE_TAGS = ("@openswe", "@open-swe", "@openswe-dev", "@seahaven-openswe")
|
||||
UNTRUSTED_GITHUB_COMMENT_OPEN_TAG = "<dangerous-external-untrusted-users-comment>"
|
||||
UNTRUSTED_GITHUB_COMMENT_CLOSE_TAG = "</dangerous-external-untrusted-users-comment>"
|
||||
_SANITIZED_UNTRUSTED_GITHUB_COMMENT_OPEN_TAG = "[blocked-untrusted-comment-tag-open]"
|
||||
|
|
|
|||
|
|
@ -1,23 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from agent.utils.authorship import (
|
||||
OPEN_SWE_BOT_EMAIL,
|
||||
OPEN_SWE_BOT_NAME,
|
||||
add_bot_coauthor_trailer,
|
||||
resolve_triggering_user_identity,
|
||||
)
|
||||
|
||||
_BOT_TRAILER = f"Co-authored-by: {OPEN_SWE_BOT_NAME} <{OPEN_SWE_BOT_EMAIL}>"
|
||||
|
||||
|
||||
def test_add_bot_coauthor_trailer_appends_bot() -> None:
|
||||
result = add_bot_coauthor_trailer("fix: thing")
|
||||
assert result == f"fix: thing\n\n{_BOT_TRAILER}"
|
||||
|
||||
|
||||
def test_add_bot_coauthor_trailer_is_idempotent() -> None:
|
||||
once = add_bot_coauthor_trailer("fix: thing")
|
||||
assert add_bot_coauthor_trailer(once) == once
|
||||
from agent.utils.authorship import resolve_triggering_user_identity
|
||||
|
||||
|
||||
def test_resolve_identity_from_config_uses_user_noreply_email() -> None:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ from agent.utils.authorship import (
|
|||
OPEN_SWE_BOT_EMAIL,
|
||||
OPEN_SWE_BOT_NAME,
|
||||
CollaboratorIdentity,
|
||||
add_pr_collaboration_note,
|
||||
resolve_triggering_user_identity,
|
||||
)
|
||||
|
||||
|
|
@ -100,7 +99,7 @@ def test_construct_system_prompt_includes_corridor_prompt_when_enabled() -> None
|
|||
def test_construct_system_prompt_omits_collaboration_section_without_identity() -> None:
|
||||
prompt = construct_system_prompt(working_dir="/workspace")
|
||||
|
||||
assert "Collaborative Attribution" not in prompt
|
||||
assert "Authorship & Attribution" not in prompt
|
||||
assert "Co-authored-by:" not in prompt
|
||||
|
||||
|
||||
|
|
@ -136,7 +135,7 @@ def test_construct_system_prompt_forbids_force_push() -> None:
|
|||
assert "git pull --rebase origin <branch>" in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_includes_coauthor_trailer_when_identity_present() -> None:
|
||||
def test_construct_system_prompt_emits_no_attribution_when_identity_present() -> None:
|
||||
identity = CollaboratorIdentity(
|
||||
display_name="octocat",
|
||||
commit_name="octocat",
|
||||
|
|
@ -148,16 +147,24 @@ def test_construct_system_prompt_includes_coauthor_trailer_when_identity_present
|
|||
triggering_user_identity=identity,
|
||||
)
|
||||
|
||||
assert "Collaborative Attribution" in prompt
|
||||
# The user authors the commits; open-swe[bot] is the co-author/collaborator.
|
||||
# The Sea Haven Authorship section renders, the commits are still authored as
|
||||
# the triggering user (identity flip to the bot is deferred — see issue #11),
|
||||
# and NO agent/AI attribution leaks into the prompt.
|
||||
assert "Authorship & Attribution" in prompt
|
||||
assert "Add NO agent or AI attribution" in prompt
|
||||
# Values are shell-escaped via shlex.quote; safe tokens need no quoting.
|
||||
assert "git config user.name octocat" in prompt
|
||||
assert "git config user.email 1234+octocat@users.noreply.github.com" in prompt
|
||||
assert _BOT_TRAILER in prompt
|
||||
assert "Made by [Open SWE](https://openswe.vercel.app)" in prompt
|
||||
# The concrete attribution artifacts the old template INSTRUCTED are gone (the
|
||||
# prohibition still names them as examples, so assert the instructional forms:
|
||||
# the full co-author trailer, the URL-bearing footer, and the old mandate text).
|
||||
assert _BOT_TRAILER not in prompt
|
||||
assert "Made by [Open SWE](https://openswe.vercel.app)" not in prompt
|
||||
assert "Credit open-swe as the collaborator" not in prompt
|
||||
assert "append this trailer" not in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_includes_github_login_in_pr_footer() -> None:
|
||||
def test_construct_system_prompt_no_attribution_with_github_login() -> None:
|
||||
identity = CollaboratorIdentity(
|
||||
display_name="Mona Lisa",
|
||||
commit_name="Mona Lisa",
|
||||
|
|
@ -173,27 +180,19 @@ def test_construct_system_prompt_includes_github_login_in_pr_footer() -> None:
|
|||
# A name with a space is shlex-quoted; the safe email is left bare.
|
||||
assert "git config user.name 'Mona Lisa'" in prompt
|
||||
assert "git config user.email 1234+octocat@users.noreply.github.com" in prompt
|
||||
assert _BOT_TRAILER in prompt
|
||||
assert "Made by [Open SWE](https://openswe.vercel.app)" in prompt
|
||||
assert "replace that existing footer with this line" in prompt
|
||||
assert "`_Opened collaboratively by Mona Lisa and open-swe._`" in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_footer_links_thread_when_provided() -> None:
|
||||
identity = CollaboratorIdentity(
|
||||
display_name="octocat",
|
||||
commit_name="octocat",
|
||||
commit_email="1234+octocat@users.noreply.github.com",
|
||||
)
|
||||
|
||||
prompt = construct_system_prompt(
|
||||
working_dir="/workspace",
|
||||
triggering_user_identity=identity,
|
||||
thread_url="https://openswe.vercel.app/agents/abc-123",
|
||||
)
|
||||
|
||||
assert "Made by [Open SWE](https://openswe.vercel.app/agents/abc-123)" in prompt
|
||||
assert _BOT_TRAILER not in prompt
|
||||
assert "Made by [Open SWE](https://openswe.vercel.app)" not in prompt
|
||||
assert "replace that existing footer with this line" not in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_uses_sea_haven_conventions() -> None:
|
||||
prompt = construct_system_prompt(working_dir="/workspace")
|
||||
|
||||
# Branch naming, PR structure, and commit format follow the handbook.
|
||||
assert "feature/" in prompt and "hotfix/" in prompt
|
||||
assert "Do NOT use a conventional-commit `type:` prefix" in prompt
|
||||
assert "## Summary" in prompt and "## Validation" in prompt
|
||||
assert "## Release Note" not in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_shell_escapes_user_name() -> None:
|
||||
|
|
@ -217,38 +216,6 @@ def test_construct_system_prompt_shell_escapes_user_name() -> None:
|
|||
assert f"git config user.name {hostile}" not 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\nMade by [Open SWE](https://openswe.vercel.app)"
|
||||
)
|
||||
|
||||
|
||||
def test_add_pr_collaboration_note_links_thread() -> None:
|
||||
body = "## Description\nDone."
|
||||
|
||||
assert add_pr_collaboration_note(
|
||||
body, thread_url="https://openswe.vercel.app/agents/abc-123"
|
||||
) == ("## Description\nDone.\n\nMade by [Open SWE](https://openswe.vercel.app/agents/abc-123)")
|
||||
|
||||
|
||||
def test_add_pr_collaboration_note_skips_when_footer_present_with_other_link() -> None:
|
||||
body = "## Description\nDone.\n\nMade by [Open SWE](https://openswe.vercel.app)"
|
||||
|
||||
assert (
|
||||
add_pr_collaboration_note(body, thread_url="https://openswe.vercel.app/agents/abc-123")
|
||||
== body
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_triggering_user_identity_combines_slack_name_with_github_login() -> None:
|
||||
identity = resolve_triggering_user_identity(
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue