Skip to content

Fix: stop hard-resetting source/ on every chat tick#1860

Draft
Wirasm wants to merge 3 commits into
devfrom
archon/task-bench-1516-claude
Draft

Fix: stop hard-resetting source/ on every chat tick#1860
Wirasm wants to merge 3 commits into
devfrom
archon/task-bench-1516-claude

Conversation

@Wirasm

@Wirasm Wirasm commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Problem: On every non-whitelisted chat message with an attached managed-clone codebase, discoverAllWorkflows() called syncWorkspace() with resetAfterFetch: true, which ran git reset --hard origin/<default_branch> against source/. Any uncommitted edits, local commits, or non-default-branch checkouts in source/ were silently destroyed before the AI executed — creating an infinite redo loop where turn-N work was gone by turn-N+1.
  • Why it matters: Agents actively writing to source/ in Web Chat mode could never make lasting progress; users who registered a local repo or checked out a feature branch lost all in-progress state on every message.
  • What changed: syncWorkspace gains a mode: 'fast-forward' | 'reset' option (default 'fast-forward'). Fast-forward mode inspects branch state and only runs git merge --ff-only when strictly safe (clean tree, on target branch, behind origin). The chat-tick discovery path now uses the default (non-destructive). Worktree creation explicitly passes mode: 'reset' — the one legitimate use of a hard reset. A new post-message-reminder emits a system-status notice when source/ has unpushed commits or uncommitted changes after a chat turn. default_branch is stored in the DB at clone/register time to avoid re-detecting on every tick.
  • What did not change: Workflow execution paths (already isolated in worktrees), CLI behavior, forge adapter syncRepository, and anything related to issue Web UI silently hydrates long conversations to oldest 200 messages — newest messages disappear after refresh or mid-session SSE recovery (DB is OK, UI bug) #1531.

UX Journey

Before

User                    Archon                         source/
────                    ──────                         ───────
sends message ────────▶ discoverAllWorkflows()
                          syncWorkspace(resetAfterFetch=true)
                          git fetch + git reset --hard ─────▶ local commits GONE
                                                               uncommitted edits GONE
                                                               branch forced to main
                        AI executes against clean state
                        AI re-applies its own prior work
                        streams response
sees reply ◀──────────  (agent output, but work is gone next tick)

After

User                    Archon                         source/
────                    ──────                         ───────
sends message ────────▶ discoverAllWorkflows()
                          syncWorkspace(mode='fast-forward' [default])
                          git fetch
                          inspect state (dirty? ahead? diverged?)
                          ff-merge only if safe ─────▶ local commits PRESERVED
                                                        uncommitted edits PRESERVED
                                                        branch unchanged
                        AI executes against real state
                        streams response
sees reply ◀──────────  [system_status if unpushed work detected]
                          "source/ has 3 unpushed commits on feature-x. Push to preserve."

Architecture Diagram

Before

orchestrator-agent.ts
  discoverAllWorkflows()
    syncWorkspace(resetAfterFetch=isManagedClone)
      git fetch
      if resetAfterFetch: git reset --hard origin/<branch>   ← destroys state

isolation/worktree.ts
  syncWorkspaceBeforeCreate()
    syncWorkspace(resetAfterFetch=isManagedClone)
      git fetch
      if resetAfterFetch: git reset --hard origin/<branch>   ← legitimate

After

orchestrator-agent.ts
  discoverAllWorkflows()
    syncWorkspace(mode='fast-forward' [default])     [~]
      git fetch
      inspect: dirty? → return 'dirty', no mutation
      inspect: in_sync/ahead/diverged → no mutation
      behind + on target branch + clean → git merge --ff-only   [~]

isolation/worktree.ts
  syncWorkspaceBeforeCreate()
    syncWorkspace(mode='reset')                      [~]
      git fetch
      git reset --hard origin/<branch>              ← explicit, intentional

orchestrator-agent.ts
  handleMessage() (after AI turn)
    reportUnpushedWorkInSource()                     [+]
      getCurrentBranch() + countCommitsAhead() + hasUncommittedChanges()
      emit system MessageChunk if unpushed state detected

Connection inventory:

From To Status Notes
orchestrator-agent.ts syncWorkspace modified resetAfterFetchmode (default ff)
orchestrator-agent.ts post-message-reminder.ts new calls reportUnpushedWorkInSource after AI turn
post-message-reminder.ts @archon/git (getCurrentBranch, countCommitsAhead, hasUncommittedChanges) new state inspection helpers
isolation/worktree.ts syncWorkspace modified explicit mode: 'reset' for managed clones
packages/git/src/repo.ts syncWorkspace modified replaces boolean flag with mode enum
packages/git/src/branch.ts modified adds getCurrentBranch, countCommitsAhead
packages/git/src/types.ts modified adds WorkspaceSyncState, state? on result
packages/core/src/handlers/clone.ts getCurrentBranch new detects default branch at clone/register time
packages/core/src/db/codebases.ts modified stores/reads default_branch
migrations/023_add_default_branch_to_codebases.sql new adds nullable column

Label Snapshot

  • Risk: risk: medium
  • Size: size: M
  • Scope: core, git, isolation
  • Module: core:orchestrator, git:repo, isolation:worktree

Change Metadata

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

Linked Issue

Validation Evidence (required)

bun run validate

All seven checks pass:

Check Result
bun run check:bundled ✅ Pass (36 commands, 20 workflows)
bun run check:bundled-skill ✅ Pass (21 files)
bun run check:bundled-schema ✅ Pass
bun run type-check ✅ Pass (all 10 packages)
bun run lint --max-warnings 0 ✅ 0 errors, 0 warnings
bun run format:check ✅ Pass
bun run test ✅ 4375 passed, 0 failed
  • Evidence provided: Full bun run validate output — all clean.
  • No commands were skipped.

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No — syncWorkspace now touches source/ less (no more hard reset), reducing blast radius.

Compatibility / Migration

  • Backward compatible? Yes — callers that passed { resetAfterFetch: true } are the one call site in worktree.ts, now updated to { mode: 'reset' }. No external API changes.
  • Config/env changes? No
  • Database migration needed? Yes — migrations/023_add_default_branch_to_codebases.sql adds a nullable default_branch TEXT column. Existing rows get NULL; the orchestrator falls back to getDefaultBranch() auto-detect for those rows, preserving pre-PR behavior.
  • Upgrade steps: None required — migration runs automatically on first startup.

Human Verification (required)

Automated test suite covers all five syncWorkspace fast-forward states (dirty, in_sync, ahead, behind, diverged), the off-branch guard, and regression guard that the chat-tick path never passes mode: 'reset'. Manual scenario verification:

Side Effects / Blast Radius (required)

  • Affected subsystems: @archon/git (new helpers + mode enum), @archon/core orchestrator + DB + clone handler, @archon/isolation worktree creation. Workflow execution paths are untouched.
  • Potential unintended effects: Managed clones that were previously kept at origin/main via hard reset will no longer be force-synced on every chat tick. If source/ is used as a passive mirror, it may now drift from origin. That is the intended trade-off (prefer not-destroying-data over aggressive sync).
  • Guardrails: The post-message-reminder surfaces any local-only state to the user immediately after each turn, so they can push before creating a worktree.

Rollback Plan (required)

  • Fast rollback: Revert commit 2d5aed12. The change is fully self-contained; reverting restores the old resetAfterFetch: boolean interface and removes the post-message reminder.
  • Feature flags: None.
  • Observable failure symptoms: If source/ drifts from origin/<default_branch> in ways that cause workflow YAML reads to fail, workflow discovery errors will surface in /workflow list. syncWorkspace returning state: 'diverged' would appear in debug logs.

Risks and Mitigations

  • Risk: Existing callers of syncWorkspace using { resetAfterFetch: true } break at the type level after this rename.
    • Mitigation: Grep confirmed the only in-repo caller was worktree.ts — updated in this PR. resetAfterFetch removed from the type signature.
  • Risk: Fast-forward merge on a dirty working tree silently skips the update, leaving source/ stale.
    • Mitigation: state: 'dirty' is returned; the caller (orchestrator) already skips the update path on non-updated results. The post-message-reminder warns the user.
  • Risk: default_branch NULL for existing rows causes a regression on the stored-branch fast-path.
    • Mitigation: Explicit ?? undefined fallback passes undefined to syncWorkspace, which auto-detects via getDefaultBranch() — identical to pre-PR behavior.

Every non-whitelisted chat message ran `syncWorkspace(..., {
resetAfterFetch: true })` on the canonical source/ for Archon-managed
clones, hard-resetting any agent edits, local commits, or non-default
branches before the next turn. The agent then re-did its own work,
entering a redo loop.

Changes:
- Replace `resetAfterFetch: boolean` with `mode: 'fast-forward' | 'reset'`
  in `syncWorkspace`. Default `'fast-forward'` is non-destructive: it
  fetches, inspects state, and only advances HEAD via `git merge --ff-only`
  when strictly behind on the target branch with a clean tree. `'ahead'`,
  `'diverged'`, and `'dirty'` states are preserved.
- Add `WorkspaceSyncState` and report it on `WorkspaceSyncResult`.
- Add `getCurrentBranch` and `countCommitsAhead` git helpers.
- Chat-tick discovery in `orchestrator-agent.ts` now uses the new default
  (fast-forward) — managed vs. unmanaged distinction removed for chat.
- Worktree creation in `@archon/isolation` keeps `mode: 'reset'` for
  managed clones (the legitimate destructive-mirror caller).
- Add `default_branch` column to codebases (migration 023, bundled-schema
  regenerated, SQLite `migrateColumns` entry). Stored at clone/register
  time via `getCurrentBranch`; chat sync passes it through instead of
  re-detecting on every message.
- Add `post-message-reminder.ts`: after a direct-chat turn that touches a
  managed source/, surface a one-line `system` event if there are
  uncommitted edits or unpushed commits, so users know to push/commit
  before the next worktree creation reclaims that work.
- Update git/isolation/orchestrator tests for the new API; add coverage
  for all five fast-forward states (in_sync/dirty/ahead/behind/diverged),
  the off-branch behind guard, and the managed-clone `mode: 'reset'` path.

Fixes #1516
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3151a2b-650f-4e5f-aede-185289859bbd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch archon/task-bench-1516-claude

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.

@Wirasm

Wirasm commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

Comprehensive PR Review

PR: #1860 — Fix: stop hard-resetting source/ on every chat tick
Reviewed by: 4 specialized agents (code-review, error-handling, test-coverage, docs-impact)
Date: 2026-06-04


Summary

The core fix is solid. The mode: 'fast-forward' | 'reset' API is clean and unambiguous, the 5-state fast-forward logic is correct, and post-message-reminder.ts is well-isolated and non-fatal by construction. The main blockers are two missing test surfaces: the entirely new post-message-reminder.ts module has zero dedicated tests, and the two new exported git helpers (getCurrentBranch, countCommitsAhead) lack their own describe blocks despite every other function in branch.ts having them.

Verdict: REQUEST_CHANGES

Severity Count
🔴 HIGH 2
🟡 MEDIUM 2
🟢 LOW 7

🔴 HIGH Issues (Should fix before merge)

HIGH-1: post-message-reminder.ts — entirely new module with zero dedicated tests

📍 packages/core/src/orchestrator/post-message-reminder.ts

The orchestrator test mocks @archon/git so getCurrentBranch returns null, making the reminder exit early at line 40 — the only path exercised. None of these branches are tested:

  • Path guard: skipping non-managed repos (cwdNormalized.startsWith(archonWorkspacesPath))
  • !platform.sendStructuredEvent guard
  • ahead > 0 && !dirty → emit unpushed-commit count
  • ahead === 0 && dirty → emit uncommitted-changes message
  • Both true → composite message
  • SSE event type 'system' and content format
  • catch block that logs at warn and swallows

The core invariant — "fires when ahead > 0 or dirty, silent otherwise" — is never asserted. If the Windows path-separator guard is wrong, the reminder either fires for all repos or never fires for managed repos, silently.

Suggested test skeleton
// packages/core/src/orchestrator/post-message-reminder.test.ts
import { mock, describe, test, expect, beforeEach } from 'bun:test';

const mockGetCurrentBranch = mock(() => Promise.resolve(null));
const mockCountCommitsAhead = mock(() => Promise.resolve(0));
const mockHasUncommittedChanges = mock(() => Promise.resolve(false));

mock.module('@archon/git', () => ({
  getCurrentBranch: mockGetCurrentBranch,
  countCommitsAhead: mockCountCommitsAhead,
  hasUncommittedChanges: mockHasUncommittedChanges,
  toRepoPath: (p: string) => p,
}));

mock.module('@archon/paths', () => ({
  getArchonWorkspacesPath: () => '/home/test/.archon/workspaces',
  createLogger: () => ({ warn: mock(() => undefined), error: mock(() => undefined) }),
}));

import { reportUnpushedWorkInSource } from './post-message-reminder';

// Tests: no sendStructuredEvent, non-managed path, detached HEAD (null branch),
// clean+in-sync (no emit), ahead > 0 (emit with count), dirty (emit), error swallowed

Add as a separate bun test invocation in packages/core/package.json to avoid mock pollution.


HIGH-2: getCurrentBranch + countCommitsAhead — exported functions with no unit tests

📍 packages/git/src/branch.ts:321-360, packages/git/src/git.test.ts

Every other function in branch.ts has its own describe block (hasUncommittedChanges, getDefaultBranch, commitAllChanges, isBranchMerged, isPatchEquivalent, isAncestorOf, getLastCommitDate). These two are the only exceptions despite being exported public API.

The fail-silent contracts (return null/0 on any error) are critical to the fast-forward guard and the reminder. If a future refactor removes the catch, syncWorkspaceFastForward would throw on detached-HEAD repos, breaking the non-destructive sync guarantee silently.

Suggested describe blocks (spyOn pattern, same as surrounding tests)
describe('getCurrentBranch', () => {
  let execSpy: Mock<typeof git.execFileAsync>;
  beforeEach(() => { execSpy = spyOn(git, 'execFileAsync'); });
  afterEach(() => { execSpy.mockRestore(); });

  test('returns branch name on clean symbolic-ref output', async () => {
    execSpy.mockResolvedValue({ stdout: 'main\n', stderr: '' });
    expect(await git.getCurrentBranch('/workspace/repo')).toBe('main');
  });
  test('returns null for empty stdout (detached HEAD)', async () => {
    execSpy.mockResolvedValue({ stdout: '', stderr: '' });
    expect(await git.getCurrentBranch('/workspace/repo')).toBeNull();
  });
  test('returns null on any git error (ENOENT, not a repo)', async () => {
    execSpy.mockRejectedValue(new Error('fatal: not a git repository'));
    expect(await git.getCurrentBranch('/workspace/repo')).toBeNull();
  });
});

describe('countCommitsAhead', () => {
  let execSpy: Mock<typeof git.execFileAsync>;
  beforeEach(() => { execSpy = spyOn(git, 'execFileAsync'); });
  afterEach(() => { execSpy.mockRestore(); });

  test('returns parsed integer', async () => {
    execSpy.mockResolvedValue({ stdout: '3\n', stderr: '' });
    expect(await git.countCommitsAhead('/workspace/repo', 'main')).toBe(3);
  });
  test('returns 0 for NaN output (malformed git output)', async () => {
    execSpy.mockResolvedValue({ stdout: 'not-a-number\n', stderr: '' });
    expect(await git.countCommitsAhead('/workspace/repo', 'main')).toBe(0);
  });
  test('returns 0 on any error', async () => {
    execSpy.mockRejectedValue(new Error("fatal: unknown revision 'origin/main..HEAD'"));
    expect(await git.countCommitsAhead('/workspace/repo', 'main')).toBe(0);
  });
});

🟡 MEDIUM Issues (Needs decision)

MEDIUM-1: Codebase fetched twice per chat tick — redundant DB round-trip

📍 packages/core/src/orchestrator/orchestrator-agent.ts:1140-1152

handleMessage fetches the codebase inside discoverAllWorkflows (line 604), then fetches it again at line 1140 to pass to reportUnpushedWorkInSource. Two DB round-trips per non-slash-command tick. On Postgres this is a real network round-trip on every message.

DiscoverResult already carries syncResult, syncError, and config computed inside discoverAllWorkflows. Adding codebase?: Codebase | null is consistent with this pattern and eliminates the second fetch cleanly.

Options: Fix now (LOW effort — small interface extension) | Create issue | Skip (negligible on SQLite)


MEDIUM-2: clone.ts backfill guard untested — wrong condition could silently overwrite configured branch

📍 packages/core/src/handlers/clone.ts:175-179

if (!existing.default_branch && detectedBranch) {
  updates.default_branch = detectedBranch;
}

This guard is critical. If the condition were wrong (e.g., || instead of &&), a re-register would silently overwrite a manually configured default_branch, causing every chat tick to sync to the wrong branch. clone.ts has no test file.

Options: Fix now (MED effort — new clone.test.ts) | Create issue (acceptable — condition is simple and reviewable manually)


🟢 LOW Issues

View 7 low-priority suggestions
# Issue Location Suggestion
L1 state: 'behind' returned after successful ff-merge (updated: true but state still 'behind') packages/git/src/repo.ts:277-285 Return state: 'in_sync' post-merge; updated: true already signals the advance
L2 Redundant ?? null in detectDefaultBranchgetCurrentBranch already returns BranchName | null packages/core/src/handlers/clone.ts:139-142 return getCurrentBranch(toRepoPath(targetPath)) directly
L3 default_branch uses nullable().optional() while all other nullable columns use only nullable() packages/core/src/schemas/codebase.ts:20 Use z.string().nullable() — column is TEXT DEFAULT NULL, never undefined
L4 getCurrentBranch silent-catch swallows unexpected errors (EACCES, ETIMEDOUT) with no log entry packages/git/src/branch.ts:321-336 Add getLog().debug({ workingPath, err }, 'get_current_branch_failed') in the catch
L5 revParse failure returns 'in_sync' in debug logs when actual state is unknown packages/git/src/repo.ts Add getLog().debug({ local, remote }, 'sync_workspace_rev_parse_inconclusive_skipping_merge')
L6 updateCodebase with default_branch not covered — column typo or null-vs-undefined would be invisible packages/core/src/db/codebases.ts:155-158 Add 2 tests to existing describe('updateCodebase', ...): update + null-clear
L7 CLAUDE.md codebases table entry omits default_branch CLAUDE.md (Database Schema) Add "; default_branch captured at clone time for non-destructive chat-tick sync"

✅ What's Good

  • mode: 'fast-forward' | 'reset' API is clean and unambiguous. Named modes make intent explicit at every call site; the safe ?? 'fast-forward' default is the right conservative choice.
  • All 5 fast-forward states are covered. The regression guard test ("chat tick never passes mode: 'reset'") is exactly the right contract test for the original bug git reset --hard origin/<default_branch> on source/ every message — destroys any local state that has diverged from origin for managed clones #1516.
  • Error paths for the sync refactor are sound. Fetch errors are non-fatal; syncError captures the message for the AI's context. Double-boundary pattern (reportUnpushedWorkInSource has its own try/catch + orchestrator outer catch) is correctly defensive.
  • Migration sequencing is correct. 023_add_default_branch_to_codebases.sql uses IF NOT EXISTS, is present in 000_combined.sql, and numbering is sequential.
  • worktree.ts correctly distinguishes managed vs. non-managed clones. mode: 'reset' for managed clones, mode: 'fast-forward' for local repos — logic is clear and tested.
  • detectDefaultBranch error contract is clean. Returns null for all error cases; callers store NULL; NULL in DB falls back to runtime detection. Safe at every layer.

Suggested Follow-up Issues

Issue Title Priority
"Add tests for clone.ts default_branch detection and backfill logic" P2
"Add updateCodebase tests for default_branch field" P3
"Fix state: 'behind' returned after successful fast-forward merge" P3

Reviewed by Archon comprehensive-pr-review workflow
Full artifacts: /Users/rasmus/.archon/workspaces/coleam00/Archon/artifacts/runs/29af962b000d1be0108709498fcf5f30/review/

Tests added:
- post-message-reminder.test.ts: 9 tests covering all branches (path
  guard, no-sendStructuredEvent, detached HEAD, clean/sync, ahead only,
  dirty only, both, error swallowed)
- git.test.ts: getCurrentBranch (5 tests) and countCommitsAhead (4
  tests) describe blocks with fail-silent contract verification
- codebases.test.ts: 2 tests for updateCodebase with default_branch
  (set value and null-clear)

Code fixes:
- orchestrator-agent.ts: eliminate redundant codebase DB fetch per tick
  by carrying codebase through DiscoverResult; use discoveredCodebase
  at reminder call site
- codebase.ts schema: remove .optional() from default_branch — column
  is TEXT DEFAULT NULL, never undefined
- clone.ts: remove redundant ?? null in detectDefaultBranch
- branch.ts: add debug log in getCurrentBranch catch for unexpected
  errors (permission denied, timeout) — keeps fail-silent contract,
  adds breadcrumb
- repo.ts: add debug log when revParse returns null (inconclusive state
  was silently logged as in_sync); return state: 'in_sync' after a
  successful ff-merge where HEAD advanced (was 'behind')

Docs:
- CLAUDE.md: add default_branch to codebases table description

Skipped (deferred to follow-up issues):
- clone.ts default_branch backfill tests (MEDIUM-2): clone.ts has never
  had tests; adding a full test file is out of scope for this regression
  fix (per consolidated review recommendation)
@Wirasm

Wirasm commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

⚡ Self-Fix Report (Aggressive)

Status: COMPLETE
Pushed: ✅ Changes pushed to archon/task-bench-1516-claude
Commit: c04f6f1a
Philosophy: Fix everything unless clearly a new concern


Fixes Applied (10 total)

Severity Count
🔴 CRITICAL 0
🟠 HIGH 2
🟡 MEDIUM 1
🟢 LOW 7
View all fixes

HIGH

  • post-message-reminder.ts entirely untested — Added new post-message-reminder.test.ts with 9 tests covering all branches: no-sendStructuredEvent guard, non-managed path guard, detached HEAD early return, clean/sync no-op, ahead-only emit (singular and plural), dirty-only emit, both combined, error swallowed
  • getCurrentBranch and countCommitsAhead have no unit tests — Added describe('getCurrentBranch') (5 tests) and describe('countCommitsAhead') (4 tests) to git.test.ts using the established spyOn(git, 'execFileAsync') pattern

MEDIUM

  • Redundant DB fetch per chat tick — Extended DiscoverResult with codebase?: Codebase | null, carry it from discoverAllWorkflows, use discoveredCodebase at reminder call site; eliminates second getCodebase() call per tick

LOW

  • state: 'behind' after successful ff-merge — Return state: advanced ? 'in_sync' : 'behind' post-merge
  • Redundant ?? null in detectDefaultBranch — Return getCurrentBranch(...) directly
  • default_branch schema uses nullable().optional() — Changed to z.string().nullable()
  • getCurrentBranch silent-catch swallows unexpected errors — Added debug log in catch
  • revParse failure silently returns 'in_sync' — Added debug log before the early return
  • updateCodebase with default_branch untested — Added 2 tests (set value, null-clear)
  • CLAUDE.md codebases table entry missing default_branch — Updated description

Tests Added

  • packages/core/src/orchestrator/post-message-reminder.test.ts (new, 9 tests)
  • packages/git/src/git.test.ts: getCurrentBranch (5 tests) + countCommitsAhead (4 tests)
  • packages/core/src/db/codebases.test.ts: 2 tests for updateCodebase with default_branch

Skipped (1)

Finding Reason
clone.ts default_branch backfill tests (MEDIUM-2) New concern: clone.ts has never had tests; the consolidated review itself recommended "Create Issue" for this one. Follow-up: "Add tests for clone.ts default_branch detection and backfill logic" (P2)

Suggested Follow-up Issues

  1. "Add tests for clone.ts default_branch detection and backfill logic" — The backfill guard !existing.default_branch && detectedBranch is critical; a wrong condition would silently overwrite user-configured branches on re-register

Validation

✅ Type check | ✅ Lint (0 warnings) | ✅ Format | ✅ Tests (all packages pass)


Self-fix by Archon · aggressive mode · fixes pushed to archon/task-bench-1516-claude

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.

git reset --hard origin/<default_branch> on source/ every message — destroys any local state that has diverged from origin for managed clones

1 participant