Skip to content

detect_changes silently defaults to base main on repos whose default branch isn't main, reporting changed_files: 0 with no warning #1357

Closed

Description

@artaommahe

Version

codebase-memory-mcp 0.9.1-rc.1

Platform

macOS (Apple Silicon)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

standard

What happened, and what did you expect?

Called without base_branch, detect_changes reports base: main even when the repository has no main branch at all, and returns changed_files: 0 with no warning or error. On a clean working tree the result is indistinguishable from a correct "nothing has changed" answer.

This is the quiet-failure case that matters: an agent asking "what does my branch change, and what does it impact?" gets a confident empty answer. Repositories whose default branch is master, trunk, develop, or anything else silently get no impact analysis at all.

Expected: resolve the base from the repository's actual default branch / upstream (git symbolic-ref refs/remotes/origin/HEAD, or the branch's tracking ref), and if the chosen base cannot be resolved, say so — an error or an explicit base_resolved: false / warning field — rather than reporting a base that doesn't exist and an empty diff.

With an explicit --base-branch trunk the tool works correctly: on the same private monorepo it returned 17 changed files exactly matching git diff --name-only trunk...HEAD, 61 seed symbols and 33 impacted symbols. The engine is fine; only base resolution is wrong.

Reproduction

Public repro repo: https://github.com/artaommahe/codebase-memory-mcp-rc-repros — see Case D. Its default branch is trunk and it deliberately has no main branch.

git clone https://github.com/artaommahe/codebase-memory-mcp-rc-repros
cd codebase-memory-mcp-rc-repros

git rev-parse --abbrev-ref HEAD     # trunk
git rev-parse --verify main         # fatal: Needed a single revision  (no such branch)
git status --porcelain | wc -l      # 0   (clean tree)

codebase-memory-mcp cli index_repository --repo-path "$PWD" --mode full --name cbm-rc-repros
codebase-memory-mcp cli detect_changes --project cbm-rc-repros

Observed:

base: main
direction: inbound
changed_files: 0
seed_symbols: 0
impacted_total: 0
impacted_shown: 0
impacted: 0  (rows: name label hop; qn = group prefix + "." + name; nearest hops first)

No warning that main does not exist. --base-branch trunk behaves correctly on the same repo.

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Posted by Claude Code on behalf of @artaommahe

Activity

  1. artaommahe commented on Jul 30, 2026

    @artaommahe
    Author

    Three additions that make this broader than I originally framed it, all verified on 0.9.1-rc.1 (macOS arm64).

    1. origin/HEAD is ignored even when it is present. Pre-empting the obvious "your clone just lacks origin/HEAD" reading — I set it explicitly and nothing changes:

    git remote set-head origin trunk
    git symbolic-ref refs/remotes/origin/HEAD    # refs/remotes/origin/trunk
    
    codebase-memory-mcp cli detect_changes --project <p>
    #   base: main
    #   changed_files: 0

    2. The defect isn't limited to default resolution — any unresolvable base ref is silently accepted. An explicitly-passed nonexistent branch behaves identically:

    codebase-memory-mcp cli detect_changes --project <p> --base-branch zzz-does-not-exist
    #   base: zzz-does-not-exist
    #   changed_files: 0
    #   seed_symbols: 0

    So resolving the default from origin/HEAD — the fix I asked for — would only address half of it. The general form is: an unresolvable base ref yields a silent empty diff instead of an error.

    3. There is already a machine-readable tell, which makes this a contract violation rather than a missing nicety. merge_base is present when the base resolves and absent when it doesn't:

    --base-branch trunk --since HEAD~1   ->  base: HEAD~1, merge_base: ba7c566…, changed_files: […]
    default (no main branch)             ->  base: main,   changed_files: 0        # no merge_base key
    

    The tool's own description promises "RESPONSE: base + merge_base SHA". So the response silently omits a field it documents, in exactly the case where the answer is meaningless — which is both an easier thing to justify fixing and something clients could detect today as a stopgap.

    One clarification on my original framing: main is a documented default — it's in the tool's inputSchema as "base_branch": {"type": "string", "default": "main"}. So "silently defaults to" was imprecise; the accurate complaint is documented default, never validated, never falls back.


    Posted by Claude Code on behalf of @artaommahe

  2. added
    bugSomething isn't working
    ux/behaviorDisplay bugs, docs, adoption UX
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    and removed
    windowsWindows-specific issues
    on Aug 3, 2026
  3. added this to the 0.9.1-rc milestone on Aug 3, 2026
  4. DeusData commented on Aug 3, 2026

    @DeusData
    Owner

    Thank you for the non-main default-branch reproduction. Current detect_changes defaults the base to the literal main, and the Git command path suppresses reference errors before producing a sorted result. An invalid base can therefore look like a valid empty change set. I have removed the unrelated Windows label and routed this as a high-priority 0.9.1-rc correctness/UX bug; the contract should discover the repository default or return an explicit invalid-ref error.

  5. DeusData commented on Sep 26, 2026

    @DeusData
    Owner

    Thank you, @artaommahe, for the public reproduction repo, and for showing both that origin/HEAD is ignored and that any unresolvable ref was accepted!

    The second half was the more dangerous one, and it's fixed in v0.11.0 (e842fd7). An unresolvable base now returns an error instead of an empty change set, both for the implicit main on a trunk-only repo and for an explicit nonexistent branch.

    The default is still open: without base_branch, the tool assumes main rather than reading origin/HEAD. We'll switch it to the repository's real default branch and name the attempted ref in the error.

  6. DeusData commented on Oct 4, 2026

    @DeusData
    Owner

    Thanks again, @artaommahe, for the reproduction repo. It made the remaining half straightforward to finish.

    The default is now fixed as well: without base_branch, detect_changes resolves the base from origin/HEAD first, then a local main, a local master, and finally the upstream of the current branch. A trunk-only repository like yours now diffs against trunk without any extra argument, an explicit base_branch still wins, and an unresolvable base still returns an error rather than an empty change set. Four real Git fixtures cover these cases.

    Landed on main in 3a26acf (#2496); it ships with the next release.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeditor/integrationEditor compatibility and CLI integrationpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.ux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions