Add Session.codeProvider for cross-backend pairing#420
Merged
Conversation
Follow-up to #410 / ADR 0005. Lets a Corveil-tasked session pair with a GitHub or GitLab CodeBackend by storing the code-source provider independently from the task-source provider. - Add `Session.codeProvider: Provider?` (nil means "follow `provider`"). - Update `findPRLink` in SessionService to take an explicit provider parameter; resolve at the call site via `codeProvider ?? provider ?? .github`. Legacy in-line `gh pr list` fallback stays GitHub-only. - Backward-compat decode test for state.json predating CROW-414, plus persistence round-trip coverage. 🐦⬛ Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: F8E8977A-340D-4DF2-AA3C-3B1C21BCDD26
dhilgaertner
approved these changes
Jun 4, 2026
Contributor
dhilgaertner
left a comment
There was a problem hiding this comment.
Code & Security Review
Critical Issues
None.
Security Review
Strengths:
- No new external input, shell surface, or credential handling.
codeProvideris an internalProviderenum routed only toProviderManager.codeBackend(for:); the inlinegh pr listshell-out fallback is unchanged and still GitHub-only. - Backward-compatible decoding via
decodeIfPresent— legacystate.jsonwithoutcodeProviderdecodes tonilwith no migration, no crash (verified bysessionBackwardCompatDecodingWithoutCodeProvider).
Concerns:
- None.
Code Quality
- Model (
Session.swift):codeProviderfollows the exact established pattern for optional fields — synthesizedCodingKeys/encoder pick it up automatically, custom decoder mirrorslastReviewedHeadSha/autoMergeEnabledAt. Clean. findPRLink(SessionService.swift:1147): confirmed to be the only code-backend resolution site in the app (grep ofcodeBackend(for). Parameterizing it completes the scope; resolutionsession.codeProvider ?? session.provider ?? .githubis correct, andcodeBackend(for: .corveil)safely returnsnil.recoverOrphanbehavior change: the lookup shifts from hardcoded.githubtoticket.provider. This is an improvement, not a regression — a GitLab-ticketed orphan now queries the GitLab backend instead of GitHub, matching ADR 0005 intent.IssueTrackerleft unchanged: verified the threesession.providerreads (ticket-URL provider resolution:1490, closed-issue completion:2319,markInReviewgating:2547) are all task-source / issue-lifecycle decisions — none are code-backend lookups. Correctly out of scope.- Tests: round-trip, nil-defaults, and independent-persistence (
codeProvider: .gitlabalongsideprovider: .github) all cover the field well. Ran locally: SessionModelTests (16 pass), SessionRepositoryTests (11 pass).
Summary Table
| Color | Meaning | Verdict effect |
|---|---|---|
| Red | Must fix | Request changes |
| Yellow | Should fix | Request changes |
| Green | Consider | Approve allowed |
Recommendation: Approve — driven by [0 Red, 0 Yellow, 0 Green] findings. Small, focused, well-tested follow-up that faithfully implements ADR 0005 / CROW-414 with full backward compatibility.
4 tasks
dgershman
added a commit
that referenced
this pull request
Jun 4, 2026
## Summary - `AutoRespondCoordinator` selected the CLI binary name (`gh` vs `glab`) with six inline `provider == .gitlab ? "glab" : "gh"` / `if provider == .gitlab` checks. With the `TaskBackend` / `CodeBackend` split from #410 / ADR 0005 in place, this belongs on the backend. - Add `cliName: String` to `CodeBackend`; implement `"gh"` on `GitHubCodeBackend` and `"glab"` on `GitLabCodeBackend`. - Inject `ProviderManager` into `AutoRespondCoordinator`; resolve a `CodeBackend` per session via `session.codeProvider ?? session.provider` (the convention from `Session.swift` / #420). Corveil-or-unknown sessions fall back to GitHub tooling — matches the prior `?? .github` default. - Change `AutoRespondPrompts.build` and `QuickActionPrompts.build` to take a `CodeBackend` instead of a `Provider`. Prose branches still read `codeBackend.provider`; CLI tokens read `codeBackend.cliName`. Prompt text is byte-identical for every existing branch (`cliName` returns the same string previously hardcoded), so no behavior change. ## Test plan - [x] `swift build --arch arm64` — clean - [x] `swift build --arch arm64 --build-tests` — clean (`QuickActionPromptsTests`, `AutoRespondCoordinatorReviewSessionTests` updated to construct `CodeBackend` / `ProviderManager`) - [x] `rg 'provider == \.gitlab \? "glab" : "gh"' Sources/ Packages/ Tests/` returns zero matches - [ ] Test bundle execution blocked locally by `swiftpm-xctest-helper` being x86_64-only on this Xcode install (build is arm64); CI runs the suite. Closes #412
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #414
Summary
Follow-up to #410 / ADR 0005. Adds
Session.codeProvider: Provider?so a Corveil-tasked session can pair with a GitHub or GitLabCodeBackend— the protocol split from #411 already supported this in principle; the model now represents it.nilmeans "followprovider"; callers resolve withsession.codeProvider ?? session.provider. No persisted-state migration: legacy sessions decodecodeProvider = niland the lookup-site fallback routes them to the same backend they used pre-split.Changes
Sessionmodel — new optional field, init parameter, anddecodeIfPresentin the custom decoder (same pattern aslastReviewedHeadSha/autoMergeEnabledAt).SessionService.findPRLink— takes an explicitprovider:parameter; sole caller inrecoverOrphanpassessession.codeProvider ?? session.provider ?? .github. The in-linegh pr listshell-out fallback (only reached when noProviderManageris wired) stays GitHub-only — unchanged.SessionModelTests— extends round-trip / nil-defaults / default-values tests; addssessionBackwardCompatDecodingWithoutCodeProviderfor legacy JSON.SessionRepositoryTests.savePreservesAllOptionalFields— round-tripscodeProvider: .gitlabalongsideprovider: .githubto prove the field is independently persisted.IssueTrackeris intentionally unchanged: all three of itssession.providerreads are task-backend / issue-lifecycle decisions, notcodeBackendlookups.Test plan
swift test --package-path Packages/CrowCore --filter SessionModelTests— 16 tests pass (incl. new backward-compat decode).swift test --package-path Packages/CrowPersistence --filter SessionRepositoryTests— 11 tests pass.swift test --package-path Packages/CrowProvider --filter BackendsTests— sanity check on factory; 15 tests pass.swift build --arch arm64— clean build, no callers broke.state.jsonfrom a pre-CROW-414 Crow build — confirm sessions hydrate withcodeProvider == niland PR discovery still finds the right backend.🤖 Generated with Claude Code