Skip to content

feat: branch-scoped memory with git ancestry filtering#1246

Open
thedotmack wants to merge 11 commits intomainfrom
branch-memory
Open

feat: branch-scoped memory with git ancestry filtering#1246
thedotmack wants to merge 11 commits intomainfrom
branch-memory

Conversation

@thedotmack
Copy link
Owner

@thedotmack thedotmack commented Feb 26, 2026

Summary

  • Branch-scoped observations: Observations are tagged with branch and commit_sha at write time via detectCurrentBranch in the hook layer
  • Git ancestry filtering: SessionStart context and MCP search results are filtered to only show observations from commits that are ancestors of the current HEAD
  • Graceful degradation: Non-git directories, detached HEAD, shallow clones, and resolution errors all fall back to showing all observations (no data loss)

Implementation (6 phases)

Phase What
01 Schema migration (branch/commit_sha columns), detectCurrentBranch, write path through storeObservation
02 resolveVisibleCommitShas git ancestry utility with git merge-base --is-ancestor batching
03 SessionStart ContextBuilder.generateContext() filters observations by branch ancestry
04 MCP search SearchManager.resolveBranchFilter() filters search/timeline results by ancestry
05 Edge case hardening: shallow clones, detached HEAD, worktrees, resolveViaGitLog fallback
06 Comprehensive integration tests for glue code (27 new tests, 76 assertions)

Test plan

  • PendingMessageStore branch/commit_sha lifecycle (enqueue, claim, recovery)
  • SearchManager resolveBranchFilter with direct SHAs, auto-resolution, error fallback, comma parsing
  • ContextBuilder branch resolution, non-git fallback, error recovery, multi-project aggregation
  • SDKAgent branch metadata capture from pending messages
  • Hook layer detectCurrentBranch integration (normal, detached HEAD, non-git)
  • Git ancestry: shallow clones, detached HEAD, worktrees, batching performance
  • Full suite: 1182 tests pass, zero regressions

🤖 Generated with Claude Code

thedotmack and others added 9 commits February 25, 2026 22:16
…hase 01)

Add branch and commit_sha columns to observations table via migration 24,
create git branch detection utility, and thread branch metadata through the
entire observation pipeline from hook layer through worker to database storage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ry (Phase 02)

Add git-ancestry.ts with getCurrentHead, resolveAncestorCommits, and
resolveVisibleCommitShas functions that power branch isolation by determining
which commits are ancestors of HEAD. Add getUniqueCommitShasForProject
query helper in observations/get.ts. All 10 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…(Phase 03)

Wire branch visibility resolution into the context injection pipeline so
observations from sibling branches are excluded while merged branch work
and pre-migration observations remain visible. The real cwd now flows
from the SessionStart hook through the worker API to the ContextBuilder
for accurate git ancestry resolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ry (Phase 04)

Extends branch awareness to MCP search tools (search, timeline, get_observations).
When users search memory, results are filtered to only show observations from
ancestor branches, completing the branch isolation story across all memory access
points. Supports both explicit commit_sha filtering and auto-detection via cwd.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…allow clones, and worktrees (Phase 05)

Adds comprehensive edge case tests for branch memory git utilities:
- isGitRepository: validates git repo detection for real repos, /tmp, and nonexistent paths
- detectCurrentBranch: tests detached HEAD returns null branch but valid commitSha
- Shallow clone: creates temp shallow clone and verifies ancestry checks don't throw
- Worktree: creates temp worktree and verifies isGitRepository and detectCurrentBranch work
- Additional ancestry edge cases: truncated SHAs, non-git cwd

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ance tests (Phase 05)

When resolveViaGitLog fails (e.g. shallow clone), the caller now properly
falls back to batched merge-base checks instead of returning an empty array
that would hide all observations. Added tests verifying correct behavior
across batch boundaries with >100 candidates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ckward compat, dedup, and filtering (Phase 05)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All 1157 tests pass with 0 failures across 68 files.
Build succeeds and marketplace synced.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ork queue (Phase 05)

End-to-end verification revealed that PendingMessageStore.enqueue() was not
persisting branch and commit_sha to the pending_messages table, causing these
values to be lost during the database work queue round-trip. This resulted in
all observations having NULL branch/commit_sha despite the hook correctly
detecting and sending the values.

- Add branch/commit_sha columns to pending_messages table (migration 25)
- Update PendingMessageStore.enqueue() to persist branch/commit_sha
- Update PersistentPendingMessage interface with branch/commit_sha fields
- Update toPendingMessage() to read branch/commit_sha back from DB
- Add migration to both SessionStore.ts and migrations/runner.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 26, 2026

Greptile Summary

This PR implements branch memory - a git-aware observation filtering system that creates visibility semantics similar to how git works. Observations are now tagged with the current branch and commit SHA when captured, and context generation filters observations to only show those from commits that are ancestors of the current HEAD.

Key changes:

  • Added git-branch.ts and git-ancestry.ts utilities for detecting current branch/commit and resolving ancestor relationships
  • Extended database schema with migrations 24 and 25 to add branch and commit_sha columns to observations and pending_messages tables
  • Updated observation storage path to capture and persist branch metadata through the entire pipeline (hook → pending messages → worker → database)
  • Implemented branch ancestry filtering in ContextBuilder and SearchManager using git merge-base for ancestor checking, with performance optimizations for large candidate sets (batching for 100+, git-log optimization for 500+)
  • Maintained backward compatibility: observations with NULL commit_sha (pre-migration data) are always visible regardless of current branch
  • Added comprehensive test coverage including integration tests, edge case handling (detached HEAD, shallow clones, worktrees), and filtering validation

Confidence Score: 5/5

  • This PR is safe to merge with no critical issues found
  • Score reflects thorough implementation with comprehensive test coverage (5 new test files), proper error handling throughout, maintained backward compatibility with NULL commit_sha values, performance optimizations for large repos, and clean separation of concerns. All git operations gracefully handle edge cases like non-git directories, detached HEAD, and shallow clones.
  • No files require special attention

Important Files Changed

Filename Overview
src/services/integrations/git-branch.ts New utility for detecting current git branch and commit SHA, with safe error handling for non-git directories
src/services/integrations/git-ancestry.ts New utility for resolving ancestor commits with batching optimization for large candidate sets, includes fallback to git-log for 500+ candidates
src/services/sqlite/observations/store.ts Updated storeObservation to accept and persist branch/commit_sha parameters, includes branch in dedup hash to prevent cross-branch deduplication
src/services/sqlite/observations/get.ts Added commit_sha filtering to getObservationsByIds with backward compatibility for NULL values, added getUniqueCommitShasForProject helper
src/services/sqlite/PendingMessageStore.ts Added branch and commit_sha fields to persist branch metadata through the work queue round-trip
src/services/sqlite/migrations/runner.ts Added migrations 24 and 25 to add branch/commit_sha columns to observations and pending_messages tables
src/services/context/ContextBuilder.ts Added branch ancestry resolution to filter observations based on visible commit SHAs before rendering context
src/cli/handlers/observation.ts Added branch/commit detection and passed through to worker via pending message queue
tests/branch-memory-integration.test.ts New comprehensive integration tests for branch memory write path, backward compatibility, deduplication, and filtering
tests/git-ancestry.test.ts New tests for git ancestry resolution including shallow clone handling and performance optimization paths

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start([User works in git repo]) --> Detect[detectCurrentBranch<br/>reads branch + commit SHA]
    Detect --> Hook[Hook captures observation]
    Hook --> Queue[PendingMessageStore<br/>persists branch/commit_sha]
    Queue --> Worker[Worker processes message]
    Worker --> Store[storeObservation<br/>saves to DB with branch/commit_sha]
    Store --> DB[(observations table<br/>with branch + commit_sha columns)]
    
    Request([Context generation request]) --> GetShas[getUniqueCommitShasForProject<br/>fetches all candidate SHAs]
    GetShas --> Resolve{resolveVisibleCommitShas}
    Resolve -->|Not in git repo| ShowAll[Return null<br/>show all observations]
    Resolve -->|In git repo| Ancestry[resolveAncestorCommits<br/>git merge-base checks]
    Ancestry -->|Small set| Concurrent[Concurrent checks]
    Ancestry -->|100-500| Batched[Batched processing]
    Ancestry -->|500+| GitLog[git log optimization<br/>with fallback]
    Concurrent --> Filter
    Batched --> Filter
    GitLog --> Filter
    Filter[Filter observations:<br/>commit_sha IN visible SHAs<br/>OR commit_sha IS NULL] --> Context[Render context]
    ShowAll --> Context
    
    style DB fill:#e1f5ff
    style Filter fill:#fff4e1
    style Context fill:#e8f5e9
Loading

Last reviewed commit: a371e2c

…glue code (Phase 06)

Close testing gaps in PendingMessageStore lifecycle, SearchManager branch
filtering, ContextBuilder branch resolution, SDKAgent metadata capture,
and hook layer branch detection. 27 new tests, 76 assertions, zero regressions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thedotmack thedotmack changed the title branch memory feat: branch-scoped memory with git ancestry filtering Feb 26, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant