Skip to content

feat(git): support configurable git remote name#1548

Open
deepakrawat-dce wants to merge 7 commits into
coleam00:devfrom
deepakrawat-dce:feat/configurable-git-remote
Open

feat(git): support configurable git remote name#1548
deepakrawat-dce wants to merge 7 commits into
coleam00:devfrom
deepakrawat-dce:feat/configurable-git-remote

Conversation

@deepakrawat-dce
Copy link
Copy Markdown

@deepakrawat-dce deepakrawat-dce commented May 3, 2026

Summary

  • Problem: Archon hardcodes 'origin' as the git remote name in all fetch/push/reset operations, breaking repos with non-standard remotes.
  • Why it matters: Organizations using custom-named remotes (e.g. jan, feb, mar for release-based workflows) cannot use Archon's worktree isolation at all.
  • What changed: Added worktree.remote config option, getDefaultRemote() auto-detection, and threaded remote name through all git operations.
  • What did not change (scope boundary): No changes to workflow execution, command parsing, adapter logic, or web UI. Only git/isolation plumbing.

UX Journey

Before

User                     Archon CLI                Git
────                     ──────────                ───
runs workflow ─────────▶ creates worktree
                         fetch origin <branch> ───▶ ERROR: 'origin' does not exist
                         ◀──────────────────────── fatal error
sees error ◀──────────── "Check network connection"

After

User                     Archon CLI                     Git
────                     ──────────                     ───
configures .archon/      (reads worktree.remote)
runs workflow ─────────▶ [resolveRemote()] ─────────▶  git remote
                         ◀─────────────────────────── returns 'mar'
                         fetch mar <branch> ──────────▶ SUCCESS
                         creates worktree from mar/main
sees worktree ◀────────── isolation ready

Architecture Diagram

Before

┌──────────────┐     ┌───────────────┐     ┌──────────────┐
│  CLI / Orch  │────▶│  @archon/iso  │────▶│  @archon/git │
│              │     │  worktree.ts  │     │  repo.ts     │
│              │     │               │     │  branch.ts   │
└──────────────┘     └───────────────┘     └──────────────┘
                            │
                     ┌──────┴──────┐
                     │ config-loader│
                     │ (RepoConfig) │
                     └─────────────┘

All git calls hardcode 'origin'

After

┌──────────────┐     ┌───────────────┐     ┌──────────────┐
│  CLI / Orch  │────▶│  @archon/iso  │────▶│  @archon/git │
│              │     │  worktree.ts  │     │ [~] repo.ts  │
│              │     │ [~] resolve   │     │ [~] branch.ts│
└──────────────┘     │    Remote()   │     │ [+] getDef.. │
                     └───────────────┘     └──────────────┘
                            │
                     ┌──────┴──────┐
                     │[~] config   │
                     │  +remote    │
                     └─────────────┘

All git calls use resolved remote name (config > auto-detect > error)

Connection inventory:

From To Status Notes
WorktreeProvider syncWorkspace modified Passes remote in options
WorktreeProvider getDefaultRemote new Used by resolveRemote()
WorktreeProvider execFileAsync modified Uses ${remote}/ instead of origin/
config-loader MergedConfig modified Propagates worktree.remote
syncWorkspace getDefaultBranch modified Passes remote param

Label Snapshot

  • Risk: risk: low
  • Size: size: M
  • Scope: git, isolation, config
  • Module: git:repo, isolation:worktree, core:config

Change Metadata

  • Change type: feature
  • Primary scope: multi (git + isolation + core)

Linked Issue

  • Closes: N/A (no existing issue — discovered during use)
  • Related: N/A

Validation Evidence (required)

bun test packages/git/src/git.test.ts
# 151 pass, 0 fail

bun test packages/isolation/src/providers/worktree.test.ts
# 147 pass, 0 fail

bun test packages/core/src/config/config-loader.test.ts
# 38 pass, 0 fail

# TypeScript
npx tsc --noEmit --project packages/git/tsconfig.json      # clean
npx tsc --noEmit --project packages/isolation/tsconfig.json # clean
npx tsc --noEmit --project packages/core/tsconfig.json      # clean
  • Evidence provided: All 336 tests pass, lint + prettier pass via pre-commit hooks
  • If any command is intentionally skipped: None skipped

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No (same git fetch/push, just configurable remote name)
  • Secrets/tokens handling changed? No
  • File system access scope changed? No
  • If any Yes: N/A

Compatibility / Migration

  • Backward compatible? Yes — all new parameters have defaults matching prior behavior ('origin')
  • Config/env changes? Yes — new optional worktree.remote field in .archon/config.yaml
  • Database migration needed? No
  • If yes, exact upgrade steps: N/A. Existing configs without worktree.remote behave identically.

Human Verification (required)

  • Verified scenarios: Ran workflow against a repo with non-origin remotes; confirmed getDefaultRemote() correctly auto-detects sole remote; confirmed worktree.remote config is respected
  • Edge cases checked: CRLF git output (Windows), multiple ambiguous remotes (actionable error), git failures propagating correctly, config absent (defaults to origin)
  • What was not verified: Docker environment, actual Windows host (CRLF test uses mock)

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: Worktree creation, workspace sync, branch detection, remote branch deletion
  • Potential unintended effects: None — all changes add an optional parameter with the existing 'origin' default. No behavior change for repos that already use origin.
  • Guardrails/monitoring: Existing tests cover all paths; the resolveRemote() method logs at info level when auto-detecting a non-origin remote

Rollback Plan (required)

  • Fast rollback command/path: Revert the single merge commit — all changes are additive with defaults, so reverting cleanly restores prior behavior
  • Feature flags or config toggles: The worktree.remote config field is the toggle — removing it reverts to origin behavior
  • Observable failure symptoms: "Failed to fetch base branch from ''" error during worktree creation

Risks and Mitigations

  • Risk: getDefaultRemote() now throws on git errors instead of returning null, which could surface unexpected errors in edge cases.

    • Mitigation: The function is only called inside resolveRemote() which is inside a createWorktree() try/catch that already surfaces errors with actionable messages. The error path is intentional — better to fail fast with the real error than mislead the user.
  • Risk: CRLF handling via /\r?\n/ regex could theoretically split differently on exotic git builds.

    • Mitigation: Git remote output is well-specified (one name per line). The regex handles both LF and CRLF, plus .trim() on each entry catches any trailing whitespace.

Summary by CodeRabbit

  • New Features

    • Repositories can specify a custom Git remote via worktree.remote; when omitted the remote is auto-detected (prefers "origin", else sole remote, else errors on ambiguity).
    • Workspace sync, PR worktree creation, branch creation/deletion, and cleanup now respect the configured or auto-detected remote.
  • Tests

    • Added and updated tests covering custom-remote behavior, auto-detection, error messages, and sync/reset operations.
  • Documentation

    • Configuration reference updated with remote selection and base-branch detection behavior.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds repository-level git-remote support: introduces worktree.remote in repo config and remote on relevant types, adds getDefaultRemote() auto-detection, parameterizes git helpers and worktree lifecycle to use a resolved remote for fetch/reset/remote-url/default-branch logic, and updates tests/docs accordingly. (34 words)

Changes

Git Remote Configurability

Layer / File(s) Summary
Data Shape
packages/core/src/config/config-types.ts, packages/isolation/src/types.ts
Adds remote?: string to RepoConfig.worktree and MergedConfig; adds remote?: string to WorktreeCreateConfig and WorktreeDestroyOptions with docs describing auto-detection and failure conditions.
Configuration Loading
packages/core/src/config/config-loader.ts
mergeRepoConfig() trims and propagates repo.worktree.remote into the merged config result when present.
Core Git Helpers
packages/git/src/repo.ts, packages/git/src/branch.ts
Introduces `getDefaultRemote(repoPath): Promise<string
Worktree Provider Wiring
packages/isolation/src/providers/worktree.ts, packages/isolation/src/pr-state.ts
Worktree creation resolves remote (config → getDefaultRemote() → actionable error if ambiguous) and threads remote into syncWorkspaceBeforeCreate, PR and branch creation, deletion helpers; getPrState() parameterized to use the selected remote for git remote get-url.
Module Exports
packages/git/src/index.ts
Re-exports getDefaultRemote.
Orchestration
packages/core/src/orchestrator/orchestrator-agent.ts
Repository discovery/sync now loads per-repo config and passes resolved remote into syncWorkspace.
Cleanup Service
packages/core/src/services/cleanup-service.ts
Functions for determining main branch and PR safety accept/forward an optional remote.
Tests / Documentation
packages/git/src/git.test.ts, packages/isolation/src/providers/worktree.test.ts, packages/core/src/config/config-loader.test.ts, packages/docs-web/src/content/docs/reference/configuration.md
Adds/updates tests and docs: getDefaultRemote behavior, syncWorkspace remote usage and error messages, WorktreeProvider custom-remote test suite, config propagation tests, and docs describing worktree.remote resolution rules.

Sequence Diagram(s)

sequenceDiagram
    participant Config
    participant Orchestrator
    participant WorktreeProvider
    participant GitModule
    participant RepoFS

    Config->>Orchestrator: loadRepoConfig(repoPath)
    Orchestrator->>WorktreeProvider: request worktree create (repoPath, repoConf)
    alt repoConf.worktree.remote present
        WorktreeProvider->>WorktreeProvider: use configured remote (trimmed)
    else repoConf.worktree.remote missing
        WorktreeProvider->>GitModule: getDefaultRemote(repoPath)
        GitModule-->>WorktreeProvider: remote name or null
        alt remote null
            WorktreeProvider-->>RepoFS: throw actionable "ambiguous remote" error
        end
    end
    WorktreeProvider->>GitModule: syncWorkspace(workspacePath, baseBranch?, { remote })
    GitModule->>GitModule: git fetch <remote> <branch>
    GitModule->>GitModule: git reset --hard <remote>/<branch>
    WorktreeProvider->>GitModule: create branch / set-upstream using <remote>/<branch>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • coleam00/Archon#985 — modifies cleanup pipeline and cleanupMergedWorktrees/cleanupMergedEnvironments behavior.
  • coleam00/Archon#1198 — modifies WorktreeProvider creation/branch-creation flows; overlaps remote-selection and branch start-point logic.

Poem

🐇 I trimmed the remote, hopped through the tree,
origin or lone one — tell it to me.
I fetch and I reset from the named domain,
No more hardcoded hops — clarity for main.
A rabbit-approved refactor — tidy and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(git): support configurable git remote name' accurately and concisely describes the main feature: enabling configuration of git remote names instead of hardcoding 'origin'.
Description check ✅ Passed The PR description follows the template structure with all major sections completed: Summary (4-part bullets), UX Journey (Before/After flows), Architecture Diagram (Before/After with connection inventory), Label Snapshot, Change Metadata, Validation Evidence, Security Impact, Compatibility, Human Verification, Side Effects, Rollback Plan, and Risks/Mitigations. No required sections are missing.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/isolation/src/pr-state.ts (1)

42-54: ⚡ Quick win

Use getRemoteUrl() here instead of duplicating the git call.

This PR already added a remote-aware helper in @archon/git, but this path still shells out directly. Reusing the helper keeps the no-remote/error behavior centralized and avoids the two implementations drifting again.

Suggested fix
-import { execFileAsync } from '@archon/git';
+import { execFileAsync, getRemoteUrl } from '@archon/git';
-  let remoteUrl = '';
-  try {
-    const { stdout } = await execFileAsync('git', ['-C', repoPath, 'remote', 'get-url', remote], {
-      timeout: 10000,
-    });
-    remoteUrl = stdout.trim();
-  } catch (error) {
+  let remoteUrl = '';
+  try {
+    remoteUrl = (await getRemoteUrl(repoPath, remote)) ?? '';
+  } catch (error) {
     getLog().debug(
       { err: error as Error, repoPath, branch },
       'isolation.pr_state_remote_lookup_failed'
     );
     cache?.set(branch, 'NONE');
     return 'NONE';
   }

As per coding guidelines, "Use @archon/git functions for git operations; use execFileAsync (not exec) when calling git directly".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/isolation/src/pr-state.ts` around lines 42 - 54, Replace the direct
git shell-out using execFileAsync that sets remoteUrl with the shared helper
getRemoteUrl from `@archon/git`: call getRemoteUrl(repoPath, remote) (or the
helper's appropriate param order) instead of execFileAsync, assign its result to
remoteUrl, and preserve the existing error handling path (log via getLog().debug
with the caught error, set cache?.set(branch, 'NONE') and return 'NONE') so
behavior remains identical; remove the duplicated git call and import
getRemoteUrl where pr-state.ts currently references execFileAsync/remoteUrl.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/git/src/repo.ts`:
- Around line 54-60: The remotes array may contain CRLF/trailing whitespace
(e.g., "origin\r") so remotes.includes('origin') can fail; update the
construction of remotes in repo.ts (the code that builds the remotes variable)
to normalize entries by trimming each item after split (e.g., .split('\n').map(r
=> r.trim()).filter(r => r.length > 0)) and then use the normalized remotes when
checking for 'origin' (and when returning remotes[0]) so CRLF or trailing spaces
won't break detection.
- Around line 62-66: The catch block in packages/git/src/repo.ts is swallowing
any git execution error and returning null (seen around the
get_default_remote_failed log), which incorrectly signals "no default remote";
change it to rethrow or propagate the git error except in the explicit case of
"no remote configured": in the catch in the function that queries the default
remote (references repoPath, getLog(), and the get_default_remote_failed log
key), inspect the error (err.stderr / exit code) and if it clearly indicates "no
remote" return null, otherwise rethrow the error so callers see the actual git
failure instead of the ambiguous null sentinel.

---

Nitpick comments:
In `@packages/isolation/src/pr-state.ts`:
- Around line 42-54: Replace the direct git shell-out using execFileAsync that
sets remoteUrl with the shared helper getRemoteUrl from `@archon/git`: call
getRemoteUrl(repoPath, remote) (or the helper's appropriate param order) instead
of execFileAsync, assign its result to remoteUrl, and preserve the existing
error handling path (log via getLog().debug with the caught error, set
cache?.set(branch, 'NONE') and return 'NONE') so behavior remains identical;
remove the duplicated git call and import getRemoteUrl where pr-state.ts
currently references execFileAsync/remoteUrl.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d656e48b-0636-4ddf-865b-01f8ac12f6dc

📥 Commits

Reviewing files that changed from the base of the PR and between 69b2c89 and ee4bbb3.

📒 Files selected for processing (10)
  • packages/core/src/config/config-loader.ts
  • packages/core/src/config/config-types.ts
  • packages/git/src/branch.ts
  • packages/git/src/git.test.ts
  • packages/git/src/index.ts
  • packages/git/src/repo.ts
  • packages/isolation/src/pr-state.ts
  • packages/isolation/src/providers/worktree.test.ts
  • packages/isolation/src/providers/worktree.ts
  • packages/isolation/src/types.ts

Comment thread packages/git/src/repo.ts Outdated
Comment thread packages/git/src/repo.ts Outdated
@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 4, 2026

@deepakrawat-dce related to #1419 — worktree.baseBranch config vs configurable remote name share git-config concerns.

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 4, 2026

Hi @deepakrawat-dce — thanks for opening this PR.

This repository uses a PR template at .github/pull_request_template.md with several required
sections. A few of them appear to be empty or placeholder here:

  • UX Journey
  • Architecture Diagram
  • Label Snapshot
  • Change Metadata
  • Linked Issue
  • Validation Evidence
  • Security Impact
  • Compatibility / Migration
  • Human Verification
  • Side Effects / Blast Radius
  • Rollback Plan
  • Risks and Mitigations

Could you fill those out (even briefly)? The template
helps reviewers understand scope, risk, and rollback — it
speeds up review significantly.

If a section genuinely doesn't apply, just write "N/A" in
it rather than leaving it blank.

@leex279
Copy link
Copy Markdown
Collaborator

leex279 commented May 4, 2026

🔗 Related issue: This PR is related to issue #1273 ("syncWorkspace always resets managed source clone to origin/main, ignoring configured default_branch"). Supporting configurable remote names complements the branch-detection fix for that issue.

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 4, 2026

Review Summary

Verdict: blocking-issues

Your PR adds a well-scoped, well-tested feature that removes the hardcoded 'origin' assumption from Archon's git operations. The @archon/git and @archon/isolation layers are solid — the new remote-aware functions are covered by tests and the error handling is clean and actionable. The two gaps are both in @archon/core: the orchestrator and cleanup service still hardcode 'origin', so the feature silently degrades for repos configured with a custom remote. There's also no user-facing documentation for the new worktree.remote config surface.

Blocking issues

  1. packages/core/src/orchestrator/orchestrator-agent.ts:437-438syncWorkspace() called without the remote option. The orchestrator syncs the canonical repo before every workflow run; for repos with a custom remote this will fetch from the wrong remote and may cause stale-code issues. Load repoConfig.remote and pass it: syncWorkspace({ remote: repoConfig.remote ?? undefined }).

  2. worktree.remote config field undocumentedpackages/core/src/config/config-types.ts:199-218 and packages/core/src/config/config-loader.ts:458-461. Users cannot discover or configure this feature. Add documentation to packages/docs-web/src/content/docs/reference/configuration.md covering the resolution order (explicit config → auto-detect: origin → sole remote → error on ambiguity) and its effect on git fetch/push operations.

Suggested fixes

  1. packages/core/src/services/cleanup-service.ts:457getPrState() called without passing the remote. For repos with a non-origin GitHub remote, this silently returns 'NONE', so PR-merged branch cleanup is skipped. Thread config.remote from the repo config through the isSafeToRemove call chain to getPrState().

  2. packages/core/src/config/config-loader.ts:458-461mergeRepoConfig propagates worktree.remote but has no tests (the analogous worktree.baseBranch field has three). Add: 'propagates remote from repo worktree config', 'trims whitespace from remote', 'remote is undefined when not configured' under the loadConfig describe block in config-loader.test.ts.

  3. packages/docs-web/src/content/docs/reference/configuration.md:211 — "Base branch behavior" section still says "Auto-detects the default branch via git remote show origin". Update to describe the getDefaultRemote() auto-detection logic and how worktree.remote overrides it.

  4. CHANGELOG.md [Unreleased] section — missing an entry for the worktree.remote feature.

Minor / nice-to-have

  • packages/core/src/config/config-types.ts:209-210 and packages/isolation/src/types.ts:273: Comment examples "e.g. 'jan', 'feb', 'mar' for release-based remotes" are tied to one use case. Generalize or remove.
  • packages/core/src/config/config-types.ts:214: Docstring describes error-throwing behavior that lives in resolveRemote(), not this field. Rewrite as a return-value description.
  • packages/isolation/src/providers/worktree.test.ts:2361, 2401: Test assertions hardcode "Failed to fetch base branch from 'origin'" — use a regex to match the dynamic remote name.
  • packages/git/src/repo.ts:62: Redundant "(defaults to 'origin')" — remove (the function signature already says it).
  • packages/isolation/src/providers/worktree.test.ts: Add a test for createNewBranch with custom remote when request.fromBranch is set to confirm the start-point and sync behavior are correct.

Compliments

The end-to-end test coverage in worktree.test.ts (152 new lines for custom remote scenarios) is thorough and follows the project's testing patterns well. The resolveRemote() error message improvements and the getDefaultRemote() resolution logic are clean implementations with good comments. The error classification in classifyIsolationError remains consistent across the new remote-aware paths.


Reviewed via maintainer-review-pr workflow (Pi/Minimax). Aspects run: code-review, error-handling, test-coverage, comment-quality, docs-impact.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/orchestrator/orchestrator-agent.ts (1)

774-780: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Sync notification always says "origin/" even when a different remote is configured.

remote is scoped inside discoverAllWorkflows and is not surfaced through DiscoverResult, so this user-facing message will be misleading whenever worktree.remote is set to something other than 'origin'.

🛠️ Proposed fix

Add remote to DiscoverResult and thread it through:

 interface DiscoverResult {
   workflows: WorkflowWithSource[];
   errors: readonly WorkflowLoadError[];
   syncResult?: WorkspaceSyncResult;
   syncError?: string;
   config?: MergedConfig;
+  remote?: string;
 }

Inside discoverAllWorkflows, return the resolved value:

-          return { workflows, errors: allErrors, syncResult, syncError, config };
+          return { workflows, errors: allErrors, syncResult, syncError, config, remote };

At the destructuring call site (lines 752–758):

 const {
   workflows: workflowsWithSource,
   errors: workflowErrors,
   syncResult,
   syncError,
   config: discoveredConfig,
+  remote: syncRemote,
 } = await discoverAllWorkflows(conversation);

In the notification template:

-        content: `Synced with origin/${syncResult.branch} \u2014 updated ${syncResult.previousHead} \u2192 ${syncResult.newHead}`,
+        content: `Synced with ${syncRemote ?? 'origin'}/${syncResult.branch} \u2014 updated ${syncResult.previousHead} \u2192 ${syncResult.newHead}`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/orchestrator/orchestrator-agent.ts` around lines 774 - 780,
The message hardcodes "origin/" because the remote name is not included in
DiscoverResult; update the discoverAllWorkflows return type (DiscoverResult) to
include a remote string, set that remote from worktree.remote inside
discoverAllWorkflows, thread it through to where syncResult is produced (ensure
the destructuring/site that consumes discoverAllWorkflows includes remote), and
replace the hardcoded "origin/" in the platform.sendStructuredEvent payload with
the remote value from syncResult (e.g., use
syncResult.remote/${syncResult.branch}) so the notification reflects the
configured remote.
packages/core/src/services/cleanup-service.ts (1)

587-611: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add remote parameter to the cleanupMergedEnvironments wrapper's options type.

The wrapper at isolation-operations.ts:168–174 accepts { includeClosed?: boolean }, but the underlying cleanupMergedWorktrees function now accepts { includeClosed?: boolean; remote?: string }. Without updating the wrapper's type, callers using the public API cannot pass the remote option.

Fix in isolation-operations.ts
 export async function cleanupMergedEnvironments(
   codebaseId: string,
   mainPath: string,
-  options: { includeClosed?: boolean } = {}
+  options: { includeClosed?: boolean; remote?: string } = {}
 ): Promise<CleanupOperationResult> {
   return cleanupMergedWorktrees(codebaseId, mainPath, options);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/services/cleanup-service.ts` around lines 587 - 611, The
wrapper function cleanupMergedEnvironments currently types its options as {
includeClosed?: boolean } but calls cleanupMergedWorktrees which accepts {
includeClosed?: boolean; remote?: string }, so update the wrapper's options type
to include remote?: string and forward options.remote when invoking
cleanupMergedWorktrees (ensure the function signature/parameter typing for
cleanupMergedEnvironments and any related call site or exported type reflect
remote?: string so callers can pass the remote option).
🧹 Nitpick comments (1)
packages/core/src/services/cleanup-service.ts (1)

308-316: Scheduled cleanup diverges from on-demand cleanup for non-origin remotes.

runScheduledCleanup (line 311) calls getDefaultBranch(mainRepoPath) without a remote, hardcoding 'origin' behavior. After this PR, the on-demand cleanupMergedWorktrees is remote-aware, but the periodic background cycle is not. For a repo configured with worktree.remote: upstream, the scheduler could fail to detect branches as merged (if they are merged into upstream/main but haven't reached origin/main), leaving worktrees behind indefinitely.

This is pre-existing behavior not changed by this PR, but the delta is now observable. A follow-up task to thread repo-config remote loading into runScheduledCleanup's per-environment loop would close the gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/services/cleanup-service.ts` around lines 308 - 316,
runScheduledCleanup currently calls getDefaultBranch(mainRepoPath) with the
implicit 'origin' behavior and should be made remote-aware like
cleanupMergedWorktrees; in the per-environment loop, load the repo config
(repo-config/worktree.remote) for mainRepoPath and pass the configured remote
down to getDefaultBranch and/or isBranchMerged (or call cleanupMergedWorktrees
with the remote) so branch-merger checks use the correct remote (e.g., pass
remote into getDefaultBranch, isBranchMerged, and any calls referencing
env.branch_name) to ensure the scheduler detects merges against non-origin
remotes such as upstream.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/services/cleanup-service.ts`:
- Around line 448-452: The worktree remote argument isn't flowing through the
isolation cleanup path: update the IsolationResolverDeps.cleanup.getBreakdown
type to accept an optional third parameter (remote?: string), adjust the
IsolationResolver implementation (the cleanup.getBreakdown caller/implementer)
to forward the remote to the underlying getWorktreeStatusBreakdown logic, and
update the command handler call site that currently does
getWorktreeStatusBreakdown(codebase.id, codebase.default_cwd) to load the repo
config (worktree.remote) and pass that value as the third argument so merge
detection uses the correct remote.

---

Outside diff comments:
In `@packages/core/src/orchestrator/orchestrator-agent.ts`:
- Around line 774-780: The message hardcodes "origin/" because the remote name
is not included in DiscoverResult; update the discoverAllWorkflows return type
(DiscoverResult) to include a remote string, set that remote from
worktree.remote inside discoverAllWorkflows, thread it through to where
syncResult is produced (ensure the destructuring/site that consumes
discoverAllWorkflows includes remote), and replace the hardcoded "origin/" in
the platform.sendStructuredEvent payload with the remote value from syncResult
(e.g., use syncResult.remote/${syncResult.branch}) so the notification reflects
the configured remote.

In `@packages/core/src/services/cleanup-service.ts`:
- Around line 587-611: The wrapper function cleanupMergedEnvironments currently
types its options as { includeClosed?: boolean } but calls
cleanupMergedWorktrees which accepts { includeClosed?: boolean; remote?: string
}, so update the wrapper's options type to include remote?: string and forward
options.remote when invoking cleanupMergedWorktrees (ensure the function
signature/parameter typing for cleanupMergedEnvironments and any related call
site or exported type reflect remote?: string so callers can pass the remote
option).

---

Nitpick comments:
In `@packages/core/src/services/cleanup-service.ts`:
- Around line 308-316: runScheduledCleanup currently calls
getDefaultBranch(mainRepoPath) with the implicit 'origin' behavior and should be
made remote-aware like cleanupMergedWorktrees; in the per-environment loop, load
the repo config (repo-config/worktree.remote) for mainRepoPath and pass the
configured remote down to getDefaultBranch and/or isBranchMerged (or call
cleanupMergedWorktrees with the remote) so branch-merger checks use the correct
remote (e.g., pass remote into getDefaultBranch, isBranchMerged, and any calls
referencing env.branch_name) to ensure the scheduler detects merges against
non-origin remotes such as upstream.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: edd0d4b1-1289-49fb-ad66-af1eaed9ad68

📥 Commits

Reviewing files that changed from the base of the PR and between 1415e15 and c55ab6d.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • packages/core/src/config/config-loader.test.ts
  • packages/core/src/config/config-types.ts
  • packages/core/src/orchestrator/orchestrator-agent.ts
  • packages/core/src/services/cleanup-service.ts
  • packages/docs-web/src/content/docs/reference/configuration.md
  • packages/git/src/repo.ts
  • packages/isolation/src/providers/worktree.test.ts
  • packages/isolation/src/types.ts
✅ Files skipped from review due to trivial changes (3)
  • CHANGELOG.md
  • packages/core/src/config/config-loader.test.ts
  • packages/isolation/src/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/isolation/src/providers/worktree.test.ts
  • packages/git/src/repo.ts

Comment thread packages/core/src/services/cleanup-service.ts
@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 14, 2026

Review Summary

Verdict: blocking-issues

This PR adds configurable worktree.remote support — allowing Archon to work with repos whose primary remote isn't named origin. The core implementation (getDefaultRemote(), syncWorkspace integration, error context) is solid and error handling is exemplary. However, remote propagation is incomplete: four callers in cleanup and status paths still use hardcoded 'origin', which will cause incorrect behavior for non-origin remotes.

Blocking issues

  • packages/core/src/services/cleanup-service.ts:244-249: cleanupToMakeRoom doesn't pass remote to cleanupMergedWorktrees. When the make-room cleanup path triggers, PR merge checks and branch deletion use hardcoded origingetPrState will return NONE and deleteRemoteBranchTracked will push to the wrong remote. Add remote?: string param, resolve from loadRepoConfig, and forward.

  • packages/core/src/services/cleanup-service.ts:313: runScheduledCleanup calls getDefaultBranch(repoPath) without passing remote. Uses hardcoded origin for default branch detection across all worktrees — repos with non-origin defaults will detect the wrong branch. Resolve remote per-environment (from env.codebase_default_cwd via loadRepoConfig) before calling.

Suggested fixes

  • packages/isolation/src/pr-state.ts:43-44: getPrState has hardcoded 'origin' in the git remote lookup. The caller passes remote but it's ignored — PR state lookup fails for non-origin remotes, returning NONE even for GitHub PRs. Use the remote parameter instead of hardcoded 'origin'.

  • packages/core/src/operations/isolation-operations.ts:173: cleanupMergedEnvironments doesn't pass remote. CLI /worktree cleanup merged and API DELETE /isolations/cleanup/merged will use wrong remote. Add remote?: string param, resolve from loadRepoConfig(mainPath), forward via options.

  • packages/core/src/handlers/command-handler.ts:498: /worktree cleanup command calls cleanupMergedWorktrees without remote. After establishing mainPath, resolve: const remote = (await loadRepoConfig(mainPath))?.worktree?.remote?.trim() || 'origin';, then pass via options.

  • packages/core/src/handlers/command-handler.ts:1004: getWorktreeStatusBreakdown called without remote. Resolve remote (same pattern as above) before calling.

Minor / nice-to-have

  • packages/docs-web/src/content/docs/reference/configuration.md:211: Stale reference to git remote show origin — implementation now uses git symbolic-ref. Update to: "Auto-detects the default branch via git symbolic-ref on the resolved remote."

  • packages/core/src/config/config-loader.ts: Comment references "non-origin remote support" (a feature name) rather than explaining WHY. Rephrase as // Pass remote to callers for git operations.

  • CHANGELOG.md: Missing entry for the new worktree.remote configuration option under [Unreleased].

Compliments

  • Error handling is excellent throughout — new error messages include the remote name for actionable diagnostics, and the resolveRemote() fallback chain is well-documented.
  • The worktree.remote YAML example and "Remote behavior" section in configuration.md are clear and well-structured.
  • Test coverage for getDefaultRemote and custom remote scenarios in worktree.test.ts is thorough.

Reviewed via maintainer-review-pr workflow (Pi/Minimax). Aspects run: code-review, error-handling, test-coverage, comment-quality, docs-impact.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/core/src/services/cleanup-service.ts (1)

312-314: ⚡ Quick win

Cache remote resolution per codebase in scheduled cleanup.

Line 313 loads/parses repo config for every environment. With many envs on the same repo, this repeats disk/YAML work and can duplicate identical failures.

Proposed diff
 export async function runScheduledCleanup(): Promise<CleanupReport> {
   getLog().info('cleanup_started');
   const report: CleanupReport = { removed: [], skipped: [], errors: [], sessionsDeleted: 0 };
+  const remoteByRepoPath = new Map<string, string | undefined>();

   try {
@@
-        const envRemote =
-          (await loadRepoConfig(env.codebase_default_cwd)).worktree?.remote?.trim() || undefined;
+        let envRemote: string | undefined;
+        if (remoteByRepoPath.has(env.codebase_default_cwd)) {
+          envRemote = remoteByRepoPath.get(env.codebase_default_cwd);
+        } else {
+          envRemote =
+            (await loadRepoConfig(env.codebase_default_cwd)).worktree?.remote?.trim() || undefined;
+          remoteByRepoPath.set(env.codebase_default_cwd, envRemote);
+        }
         const mainBranch = await getDefaultBranch(mainRepoPath, envRemote);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/services/cleanup-service.ts` around lines 312 - 314, The
loop in cleanup-service calls loadRepoConfig(env.codebase_default_cwd) for every
env, causing repeated disk/YAML parsing and duplicate failures; add a simple
in-memory cache (e.g., Map keyed by env.codebase_default_cwd) before the call so
you only load/parse each repo once, store the resolved worktree?.remote?.trim()
(or undefined) in the cache, and then use the cached envRemote when invoking
getDefaultBranch(mainRepoPath, envRemote); ensure errors from loadRepoConfig are
cached as undefined to avoid repeated failing attempts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/src/services/cleanup-service.ts`:
- Around line 312-314: The loop in cleanup-service calls
loadRepoConfig(env.codebase_default_cwd) for every env, causing repeated
disk/YAML parsing and duplicate failures; add a simple in-memory cache (e.g.,
Map keyed by env.codebase_default_cwd) before the call so you only load/parse
each repo once, store the resolved worktree?.remote?.trim() (or undefined) in
the cache, and then use the cached envRemote when invoking
getDefaultBranch(mainRepoPath, envRemote); ensure errors from loadRepoConfig are
cached as undefined to avoid repeated failing attempts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e21559ed-64cc-4c94-b5a6-72b7f7332c4b

📥 Commits

Reviewing files that changed from the base of the PR and between c55ab6d and ad8f8d4.

📒 Files selected for processing (4)
  • packages/core/src/config/config-loader.ts
  • packages/core/src/handlers/command-handler.ts
  • packages/core/src/operations/isolation-operations.ts
  • packages/core/src/services/cleanup-service.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/core/src/config/config-loader.ts

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 25, 2026

Review Summary

Verdict: minor-fixes-needed

This PR adds a worktree.remote config option and getDefaultRemote() auto-detection — a solid, well-tested feature that makes Archon work with non-standard git remote names. The implementation is clean and the docs are updated. However, there are a few issues that need to be addressed before merging: one silent failure bug in the cleanup service, gaps in test coverage for the new remote parameter, and some valuable comments explaining safety invariants were removed.

Blocking issues

  • packages/core/src/services/cleanup-service.ts:590-620: cleanupMergedWorktrees silently catches errors from isSafeToRemove without logging. Transient failures (network, API, git) are swallowed and the environment is silently skipped — debugging will be impossible. Add a warn log before skipping.

Suggested fixes

  • packages/core/src/services/cleanup-service.ts:310-311: loadRepoConfig() is called without try/catch. A filesystem error will crash the scheduled cleanup. Wrap it and log to skip that environment.
  • Cleanup-service tests: loadRepoConfig is not mocked, so the worktree.remote-reading path is never exercised. Add mock.module('../config/config-loader', ...) with a test that returns { worktree: { remote: 'upstream' } } and asserts getDefaultBranch is called with 'upstream'.
  • Orchestrator tests: loadRepoConfig mock missing; syncWorkspace remote passthrough is never asserted. Add mock + expect.objectContaining({ remote: 'mar' }) assertion.
  • Comment in syncWorkspaceBeforeCreate + syncWorkspace: Removed comment explaining "only hard-reset managed clones; preserve uncommitted work in user-registered repos" — this safety invariant is critical for future contributors. Restore it.
  • Comments in createFromSameRepoPR + createFromForkPR: Multiple WHY-behind-behavior comments were removed (e.g., "Same-repo: use actual branch so pushes go directly to PR", "SHA: create at specific commit for reproducible reviews", "Tracking branch keeps it non-detached-HEAD"). Please restore them.

Minor / nice-to-have

  • Tests for getRemoteUrl, syncRepository, getPrState, getWorktreeStatusBreakdown, cleanupMergedWorktrees — the new remote parameter is accepted but never explicitly exercised; they all rely on the 'origin' default.
  • resolveRemote has a 30-line docstring; trim it to a one-liner.
  • createFromForkPR could use a direct test for the prSha path.
  • Docs: "Base branch behavior" section still says git remote show origin but implementation uses symbolic-ref on the resolved remote. Update wording. Also consider adding a "Troubleshooting" callout for the ambiguous-remotes error case.

Compliments

  • New getDefaultRemote() function is well-documented with a clear docstring explaining the 3-step resolution order.
  • The documentation was updated in the same PR (configuration.md) — worktree.remote field and remote behavior block are accurate and complete.
  • Test coverage for core remote resolution via worktree.test.ts custom remote support block is thorough.
  • WorktreeCreateConfig.remote JSDoc is excellent — best example of config-field documentation in this PR.
  • The PR follows CLAUDE.md consistently: explicit return types, specific named imports, structured Pino logging throughout.

Reviewed via maintainer-review-pr workflow (Pi/Minimax). Aspects run: code-review, error-handling, test-coverage, comment-quality, docs-impact.

deepak-rawat added 6 commits May 26, 2026 09:40
Archon hardcoded 'origin' as the git remote name in all fetch/push/reset
operations. This breaks for repos that use non-standard remote names (e.g.
enterprise monorepos with numbered remotes like '260', '262', '264').

Add `worktree.remote` config option in `.archon/config.yaml` and auto-detection
via `getDefaultRemote()` when not configured:
  1. 'origin' if it exists (standard convention)
  2. The sole remote if only one is configured
  3. Actionable error if ambiguous (multiple non-origin remotes)

Thread the remote name through all git operations: syncWorkspace,
syncRepository, getDefaultBranch, getRemoteUrl, worktree creation (issue/PR/
task/fork), branch tracking, and remote branch deletion.

All existing callers default to 'origin' so this is fully backwards-compatible.
Use 'jan', 'feb', 'mar' as example remote names instead of numbered
remotes that reference a specific organization.
- Split on /\r?\n/ instead of '\n' so Windows CRLF doesn't break
  origin detection (e.g. 'origin\r' fails includes('origin'))
- Let git execution errors propagate instead of swallowing them as
  null — callers now see the real failure instead of a misleading
  "set worktree.remote" message
Address review feedback:

- Load repo config before syncWorkspace in orchestrator-agent.ts and
  pass the resolved remote name
- Thread remote through cleanupMergedWorktrees → isSafeToRemove →
  getPrState so PR-state detection works for non-origin remotes
- Add getDefaultBranch remote param to getWorktreeStatusBreakdown
- Add config-loader tests: propagates remote, trims whitespace,
  undefined when not configured
- Add worktree test: fromBranch + custom remote (task workflow)
- Document worktree.remote in configuration.md reference
- Add CHANGELOG entry under [Unreleased]
- Generalize comment examples (remove org-specific language)
- Fix redundant docstring in getRemoteUrl
Address second review round:

- cleanupToMakeRoom: accept and forward remote option
- runScheduledCleanup: resolve remote per-environment via loadRepoConfig
  before calling getDefaultBranch
- command-handler /worktree cleanup: resolve remote from repo config
- command-handler /status: pass remote to getWorktreeStatusBreakdown
- isolation-operations cleanupMergedEnvironments: forward remote option
- Fix config-loader comment to describe purpose not feature name
- Add warn log in cleanupMergedWorktrees when isSafeToRemove fails
  (prevents silent swallowing of transient errors)
- Wrap loadRepoConfig in runScheduledCleanup with try/catch to prevent
  filesystem errors from crashing scheduled cleanup
- Restore safety comments: managed-clone reset invariant,
  same-repo vs fork PR rationale, SHA reproducibility, tracking branch
- Fix config-loader tests: use mockFsReadFile (upstream renamed mock)
@deepakrawat-dce deepakrawat-dce force-pushed the feat/configurable-git-remote branch from ad8f8d4 to a0e07c4 Compare May 26, 2026 04:16
@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 26, 2026

@deepakrawat-dce CI is failing, can you check?

The orchestrator test mocked @archon/git without getDefaultRemote and
../config/config-loader without loadRepoConfig, causing CI failures.
Also update syncWorkspace assertions to use objectContaining so they
don't break when additional options (like remote) are passed.
@deepakrawat-dce
Copy link
Copy Markdown
Author

@deepakrawat-dce CI is failing, can you check?

Should be resolved now. Thanks for pointing this out.

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.

3 participants