Skip to content

feat: put Cyrus PRs front and center and work directly on existing PRs and branches - #1457

Open
jakelevirne wants to merge 6 commits into
cyrusagents:mainfrom
jakelevirne:feat/pr-front-and-center
Open

jakelevirne wants to merge 6 commits into
cyrusagents:mainfrom
jakelevirne:feat/pr-front-and-center

Conversation

@jakelevirne

@jakelevirne jakelevirne commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Makes pull requests first-class in Cyrus sessions, in both directions: PRs Cyrus creates become front and center in Linear, and sessions work directly on PRs and branches that already exist instead of shadowing them.

PRs Cyrus creates — front and center in Linear:

  • The PR is linked to the issue as an attachment the moment it's created, giving the issue its Diff tab and a review page in Linear's Reviews (when the workspace's GitHub integration has code access), and the PR is pinned on the agent session header.
  • In the session timeline, simple git push and gh pr create / glab mr create / gt submit commands render as dedicated "Git Push <branch>" and "Create PR <title>" activity rows instead of generic Bash rows.
  • See docs/LINEAR_PR_REVIEWS.md (added in this PR) for the full mechanism and workspace setup.

Existing PRs and branches — sessions adopt them:

  • When an issue's branch name matches a branch that exists only on origin (an externally pushed PR branch), the session creates its worktree tracking origin/<branch> instead of cutting a fresh branch of the same name off base — which silently shadowed the remote work and caused push conflicts.
  • An open, same-repo PR referenced on the issue — as an attachment, an owner/repo#N reference in the description (including Linear's markdown-link rendering of synced-PR embeds), or a plain PR URL — has its head branch adopted as the session branch, so pushes update the existing PR. Description references outrank auto-added attachments (Cyrus attaches every PR it creates); closed and cross-fork PRs are skipped. Lookup uses the GitHub REST API: unauthenticated for public repos, GH_TOKEN/GITHUB_TOKEN when set.

Testing

  • pnpm build, pnpm typecheck, pnpm test:packages:run all pass on top of current main.
  • New unit tests cover git-command labeling, the PR-marker hook / attachment flow, remote-branch adoption, and PR-reference resolution (markdown-embed form, description-vs-attachment precedence, closed/cross-fork skips, lookup-failure fallback).
  • Validated in production on our own Cyrus deployment end-to-end: created PRs appear attached on the Linear issue with dedicated push/PR activity rows, and an issue referencing an externally pushed PR had its session check out the PR's head branch and push a commit that updated the existing PR (no duplicate PR created).

https://claude.ai/code/session_01TYbwKqXeuzViAJ3RhK1t7p

Match Linear's own coding agent: the PR Cyrus opens is attached to the
issue as a first-class GitHub-PR attachment the moment it is created
(giving the issue its Diff tab and a review page in Linear Reviews when
the GitHub integration has code access), pinned on the agent session
header, and rendered as dedicated "Git Push <branch>" / "Create PR
<title>" rows in the session timeline instead of generic Bash rows.

- claude-runner: new git-command-labels.ts recognizes simple `git push`
  and `gh pr create` / `glab mr create` / `gt submit` commands; the
  formatter uses those semantic labels for Bash action rows.
- PrMarkerHook: GitHub provider can read the PR back (`gh pr view`),
  and the hook accepts an onPullRequestDetected callback (errors are
  logged and swallowed so the session is never interrupted).
- RunnerConfigBuilder: passes onPullRequestDetected through.
- EdgeWorker: on first detection per session+PR, links the PR to the
  issue and pins it on the session; deduped, retried on failure.
- IIssueTrackerService: optional linkPullRequestToIssue and
  addAgentSessionExternalUrl; Linear implementation uses the SDK's
  attachmentLinkGitHubPR and a raw agentSessionUpdate(addedExternalUrls)
  request (the pinned SDK predates that input field).
- docs/LINEAR_PR_REVIEWS.md explains the full mechanism, workspace
  setup, the review-driven fix loop, and limitations.

Claude-Session: https://claude.ai/code/session_01GFH2uEWasM9j2MtmyK4xw5
When an issue's branch name matched a branch that existed only on
origin (an externally pushed PR branch), the local-refs-only existence
check ran before the fetch, so Cyrus cut a fresh branch off the base
branch and shadowed the remote work; later pushes then conflicted.
After the fetch, check origin for the branch and create the worktree
tracking origin/<branch> when it exists.

Claude-Session: https://claude.ai/code/session_01TYbwKqXeuzViAJ3RhK1t7p
When an issue references an open GitHub PR for the routed repo — via
attachment, a <pull-request> description embed, or a plain PR URL —
adopt the PR's head branch as the session branch instead of Linear's
generated branch name. Lookup uses the GitHub REST API (unauthenticated
for public repos, GH_TOKEN/GITHUB_TOKEN when set); closed and
cross-fork PRs are skipped, and any failure falls back to the normal
branch-name flow.

Claude-Session: https://claude.ai/code/session_01TYbwKqXeuzViAJ3RhK1t7p
Linear's API renders a synced-PR embed as [owner/repo#N](linear.app
review URL), not the <pull-request> element or a github.com URL, so
the first live run (SPE-73) found no candidates and created a fresh
PR. Match owner/repo#N references in the description, and check
description references before attachments — attachments accumulate
automatically (Cyrus attaches every PR it creates), while a
description reference is user-stated intent.

Claude-Session: https://claude.ai/code/session_01TYbwKqXeuzViAJ3RhK1t7p
@jakelevirne jakelevirne changed the title feat: put Cyrus PRs front and center in Linear agent sessions feat: put Cyrus PRs front and center and work directly on existing PRs and branches Sep 3, 2026
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.

1 participant