Skip to content

test(#5207): extend scm.Driver with fork methods - #5213

Merged
ifireball merged 4 commits into
mainfrom
agent/5207-scm-driver-fork-methods
Jul 16, 2026
Merged

test(#5207): extend scm.Driver with fork methods#5213
ifireball merged 4 commits into
mainfrom
agent/5207-scm-driver-fork-methods

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Add three fork-related methods to the scm.Driver interface and implement them in the GitHub v1 driver:

  • CreateFork: idempotent fork creation that reuses an
    existing fork via FindExistingFork before calling
    CreateFork on the forge client.
  • CommitFileToFork: commits a file to a branch on the
    fork by delegating to CreateOrUpdateFileOnBranch.
  • CreateForkChangeProposal: opens a cross-fork PR using
    forkOwner:headBranch as the head ref.

Unit tests verify idempotent fork creation, correct cross-fork PR head format, file commit targeting, and error propagation for all three methods.

Note: pre-commit could not run in the sandbox (exit 3, HTTP 403 on git fetch during hook init). The post-script runs pre-commit authoritatively on the runner.


Closes #5207

Post-script verification

  • Branch is not main/master (agent/5207-scm-driver-fork-methods)
  • Secret scan passed (gitleaks — 83397e5355462dea996845ed2a1ec019d32f30ac..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

Add three fork-related methods to the scm.Driver interface
and implement them in the GitHub v1 driver:

- CreateFork: idempotent fork creation that reuses an
  existing fork via FindExistingFork before calling
  CreateFork on the forge client.
- CommitFileToFork: commits a file to a branch on the
  fork by delegating to CreateOrUpdateFileOnBranch.
- CreateForkChangeProposal: opens a cross-fork PR using
  forkOwner:headBranch as the head ref.

Unit tests verify idempotent fork creation, correct
cross-fork PR head format, file commit targeting, and
error propagation for all three methods.

Note: pre-commit could not run in the sandbox (exit 3,
HTTP 403 on git fetch during hook init). The post-script
runs pre-commit authoritatively on the runner.

Closes #5207
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner July 16, 2026 16:57
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Jul 16, 2026
@github-actions

Copy link
Copy Markdown

E2E tests did not run

E2E tests run automatically for org/repo members and collaborators on pull requests.

For other contributors, a maintainer must add the ok-to-test label after the latest push.

See E2E testing guide for details.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Site preview

Preview: https://071e98e4-site.fullsend-ai.workers.dev

Commit: c7eb529f0567737d907be4b3e0a091bbd2d93691

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.54839% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/forge/github/github.go 88.23% 2 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@ifireball

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:26 PM UTC · Completed 5:35 PM UTC
Commit: 12bd957 · View workflow run →

@ifireball ifireball self-assigned this Jul 16, 2026
@fullsend-ai-review

Copy link
Copy Markdown

Review — approve

PR: #5213test(#5207): extend scm.Driver with fork methods
Scope: Adds three fork-related methods (CreateFork, CommitFileToFork, CreateForkChangeProposal) to the scm.Driver interface and implements them in the GitHub v1 driver with comprehensive unit tests.

Summary

Clean, well-scoped PR that implements exactly what issue #5207 authorizes. The implementation correctly uses forge.Client methods (respecting the forge abstraction), the idempotent fork-creation pattern is sound, the cross-fork PR head format (forkOwner:headBranch) is correct, and error propagation is properly handled. Tests cover all meaningful paths including idempotent reuse, new creation, and error propagation for all three methods.

Dimensions reviewed

Dimension Result
Correctness ✅ No issues — logic correct, edge cases handled, test coverage adequate
Security ✅ No issues — test-only change, no auth/permissions/secrets touched
Intent & coherence ✅ Scope matches issue #5207 exactly, test type correct per COMMITS.md, forge abstraction respected
Style & conventions ✅ No issues after challenger pass — named returns justified (disambiguates two string returns, matches forge.Client and scm.ParseRepo patterns), parameter ordering mirrors existing CreateChangeProposal
Documentation currency ⚠ 1 low finding (see below)
Cross-repo contracts ✅ No practical risk — pkg/behaviourtest is internal test infrastructure with one implementation and no external consumers

Findings

[low · documentation-staleness] docs/guides/dev/behaviour-drivers.md:9
The scm.Driver responsibility summary reads "Issues, comments, labels (via GetIssue), file commits" but the interface now also supports fork management and cross-fork change proposals. Note: this description was already incomplete before this PR (it omits CreateBranch and CreateChangeProposal which pre-date this change), so the gap is pre-existing rather than introduced.

Suggested update: "Issues, comments, labels (via GetIssue), file commits, branches, change proposals, forks"


Labels: PR extends behaviour test driver infrastructure with fork-related SCM methods

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge component/e2e End-to-end tests testing labels Jul 16, 2026

@ifireball ifireball left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/fs-fix the signature and functionality of CreateFork metohd are not fit for purpose, we need to create a fork within the same org as the repo we are forking from with a given name, the implemented function seems to create the fork within the calling user's account (impossible when called with an app token), and return the name of the created fork.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix the signature and functionality of CreateFork metohd are not fit for purpose, we need to create a fork within the same org as the repo we are forking from with a given name, the implemented function seems to create the fork within the calling user's account (impossible when called with an app token), and return the name of the created fork.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 5:54 PM UTC · Completed 6:06 PM UTC
Commit: 12bd957 · View workflow run →

The scm.Driver.CreateFork method previously forked under the
authenticated user's account (impossible with app tokens). Change the
signature to accept a forkName and create the fork within the same
org as the source repository via a new forge.Client.CreateForkInOrg
method.
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Changed scm.Driver.CreateFork to create forks within the same org as the source repo with a caller-specified name, instead of forking under the authenticated user's account. Added forge.Client.CreateForkInOrg to the interface with implementations in FakeClient and LiveClient. The new method passes 'organization' and 'name' fields to the GitHub fork API. Updated tests to verify the new signature and delegation.

Fixed (1):

  1. CreateFork signature and functionality not fit for purpose (pkg/behaviourtest/drivers/scm/driver.go): Changed scm.Driver.CreateFork signature from (ctx, owner, repo) → (forkOwner, forkRepo, err) to (ctx, owner, repo, forkName) → (forkRepo, err). The method now creates a fork within the same org as the source repository using the given name, instead of forking under the authenticated user's account (which is impossible with app tokens). Added forge.Client.CreateForkInOrg method to support org-scoped fork creation with a specified name. Updated the github.Driver implementation to delegate to CreateForkInOrg. Removed the FindExistingFork call since the GitHub API is idempotent for org forks. Updated all tests to match the new signature.

Tests: passed

Decision points
  • Added a new CreateForkInOrg method to forge.Client rather than modifying the existing CreateFork signature. (alternatives: Modify CreateFork to accept org and name params (breaks existing callers in internal/layers/commit.go), Add options struct parameter to CreateFork; rationale: The existing CreateFork serves a valid user-level fork use case in internal/layers/commit.go. Adding a new method preserves backward compatibility while enabling the org-scoped fork creation needed by the scm.Driver.)
  • Removed the FindExistingFork idempotency check from the scm.Driver.CreateFork implementation. (alternatives: Add a FindExistingForkInOrg method for org-level idempotency checking; rationale: The GitHub fork API (POST /repos/{owner}/{repo}/forks) is already idempotent — it returns 202 with existing fork metadata if the fork exists. The separate FindExistingFork call was only needed for user-level forks where the fork name might differ. With org-scoped forks using a specified name, the API handles idempotency natively.)

Updated by fullsend fix agent

@ifireball

Copy link
Copy Markdown
Member

/fs-fix what happens if the named for repo exists but is not a fork of the original repo, do we detect that and raise an error accordingly?

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:16 PM UTC · Completed 6:29 PM UTC
Commit: 12bd957 · View workflow run →

When CreateForkInOrg targets a name that already exists but is not a
fork of the source repository, return forge.ErrNotFork so callers can
handle the collision gracefully instead of receiving a generic 422.

The GitHub implementation pre-checks with GET /repos/{org}/{name}
before calling the fork API. The fake client mirrors this by checking
its Repos slice. Three new test cases cover: non-fork repo, fork of
a different source, and idempotent fork of the correct source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Added non-fork name collision detection to CreateForkInOrg. When a repository with the requested fork name already exists but is not a fork of the source repository, CreateForkInOrg now returns forge.ErrNotFork instead of proceeding to a generic GitHub 422 error. The GitHub implementation pre-checks the target repo with GET /repos/{org}/{name} before calling the fork API. Added ErrNotFork sentinel error and IsNotFork helper to forge.go, updated the FakeClient to mirror this behavior using its Repos slice and a new ForkParents map, and added three test cases covering: non-fork repo collision, fork of a different source, and idempotent fork of the correct source.

Fixed (1):

  1. No detection when named fork repo exists but is not a fork of the source: Added forge.ErrNotFork sentinel error and pre-check logic to CreateForkInOrg in the GitHub implementation. Before calling the fork creation API, the method now checks if the target repo already exists. If it exists and is not a fork, or is a fork of a different source repository, it returns ErrNotFork with a descriptive message. The FakeClient was updated to mirror this behavior. Three new test cases were added to verify all scenarios: non-fork collision, wrong-parent fork collision, and idempotent correct-parent fork.

Tests: passed

Decision points
  • Chose pre-check approach (GET repo before POST fork) over post-check (verify response after fork creation) (alternatives: Post-check: verify parent in fork creation response, Rely on GitHub's 422 error and map it to ErrNotFork; rationale: Pre-check provides a clear, specific error before the fork API call. The GitHub fork API returns a generic 422 when the name is taken by a non-fork repo, which is hard for callers to distinguish from other 422 errors. Pre-checking also avoids side effects from attempting the fork creation.)
  • Added ForkParents map to FakeClient rather than adding ParentFullName to forge.Repository (alternatives: Add ParentFullName field to forge.Repository struct; rationale: Adding a ParentFullName field to forge.Repository would expand the public API surface for all forge operations when only CreateForkInOrg needs parent info. The ForkParents map is a test-only mechanism scoped to the fake client.)

Updated by fullsend fix agent

@ifireball

Copy link
Copy Markdown
Member

/fs-fix please get the coverage for the PR to 80% or above

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:52 PM UTC · Completed 7:03 PM UTC
Commit: 12bd957 · View workflow run →

Add LiveClient httptest-based tests for CreateForkInOrg covering:
- successful fork creation when no existing repo
- ErrNotFork when existing non-fork repo has same name
- ErrNotFork when existing fork has different parent
- idempotent return for existing fork of correct source
- fallthrough to fork creation on non-200 pre-check
- API error during fork creation

Add FakeClient tests for CreateForkInOrg and IsNotFork sentinel
error to bring patch coverage above 80%.
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Added 203 lines of test code across 2 files to cover the CreateForkInOrg LiveClient implementation, the ErrNotFork sentinel error, and the FakeClient CreateForkInOrg method. The LiveClient tests use httptest servers to exercise all 6 code paths in CreateForkInOrg (pre-check non-fork collision, pre-check wrong-parent fork, pre-check correct fork idempotent return, pre-check non-200 fallthrough, successful creation, and API error). The FakeClient tests verify the same scenarios using the test double. All tests pass.

Fixed (3):

  1. Patch coverage below 80% threshold (internal/forge/github/github_test.go): Added TestCreateForkInOrg with 6 sub-tests covering all code paths in the LiveClient.CreateForkInOrg method: successful creation, ErrNotFork for non-fork collision, ErrNotFork for wrong-parent fork, idempotent return for correct fork, fallthrough on non-200 pre-check, and API error on fork creation.
  2. Missing tests for ErrNotFork sentinel error (internal/forge/fake_test.go): Added TestIsNotFork verifying the sentinel error and its wrapped form, matching the existing TestIsNotSupported pattern.
  3. Missing FakeClient tests for CreateForkInOrg (internal/forge/fake_test.go): Added TestFakeClient_CreateForkInOrg with 5 sub-tests covering: no collision, non-fork collision (ErrNotFork), wrong-parent fork (ErrNotFork), idempotent same-source fork, and injected error.

Tests: passed

Updated by fullsend fix agent

@ifireball
ifireball added this pull request to the merge queue Jul 16, 2026
Merged via the queue into main with commit a2886fe Jul 16, 2026
14 checks passed
@ifireball
ifireball deleted the agent/5207-scm-driver-fork-methods branch July 16, 2026 19:19
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 7:21 PM UTC · Completed 7:31 PM UTC
Commit: c7eb529 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

PR #5213 added three fork-related methods to the scm.Driver interface. The code agent's initial implementation delegated to forge.Client.CreateFork (user-scoped forking), incompatible with the GitHub App token deployment context. The review agent approved with no correctness findings. The human reviewer caught the design issue, leading to three fix iterations: (1) redesigning to fork within the same org via a new CreateForkInOrg method, (2) adding name collision detection, (3) adding tests for coverage. Three improvement proposals target the agents repo:

  1. Review correctness sub-agent should evaluate deployment-context compatibility (fullsend-ai/agents)
  2. Code agent should verify suggested implementation approaches are semantically compatible (fullsend-ai/agents)
  3. Fix agent should proactively meet documented coverage requirements (fullsend-ai/agents)

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/e2e End-to-end tests ready-for-merge All reviewers approved — ready to merge ready-for-review Triggers review agent dispatch testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(behaviour): extend scm.Driver with fork methods + GitHub v1 impl

1 participant