Skip to content

Feature: per-symbol git history timeline (get_node_history) — extending #257's temporal layer to symbol granularity #928

Description

@RiceTooCold

What problem does this solve?

Agents using the graph can answer what/where questions (who calls this,
where is it defined) but not why/when questions:

  • "Why is this magic number here — can I remove it?" (Chesterton's fence)
  • "When did this function last change, and what changed with it?"
  • "If I touch this symbol, what historically changes in the same commits
    that static edges don't capture?" (serializer/deserializer pairs,
    function↔test coupling)

Today the only history surface is detect_changes (present-tense diff vs a
base branch) and the file-level change_count/last_modified scalars from
#257. The per-commit detail that pass_githistory already walks is
aggregated and discarded.

This matters doubly for agent environments: CI and web-based agents
usually run on shallow clones
, where git log -L, --follow, and pickaxe
return nothing — raw git archaeology is not an option there at all.

Proposed solution

A new MCP/CLI tool get_node_history(qualified_name, project): the
evolution timeline of any line-scoped symbol (Function/Method/Class) —
every commit that touched its line range, newest first, with
author/date/subject and per-commit line stats.

Design principles (matching the project's sparse/fast ethos):

  • Lazy + cached: computed on first query via git log -L (no libgit2 —
    aligns with chore: drop optional libgit2 dependency (keep git log fallback) #865), persisted in two new SQLite tables keyed by repo HEAD.
    Warm queries never touch git.
  • Sparse storage: metadata only — patch text is never stored. Git is
    already a content-addressed store of every version; include_patch: true
    regenerates diffs on demand.
  • Lazy schema: tables created on first use — query-path opens and
    pre-existing DBs upgrade in place; unused projects never grow the tables.
  • Working-tree drift mapping: uncommitted edits above a symbol would
    silently shift its history onto the wrong lines; the handler maps the
    range back to HEAD coordinates via git diff -U0 hunk headers (same idea
    as Sourcegraph's diff adjustment); dirty files bypass the cache.
  • co_changed: true: files that most often changed in the same commits
    as this symbol — temporal coupling at symbol granularity, generalizing
    the FILE_CHANGES_WITH idea from feat: add temporal properties to FILE_CHANGES_WITH edges and File nodes #257.

Measured on this repo (~12k nodes, ~800 commits)

Metric Result
Cold query (git + parse + persist) ~58 ms
Warm query (cache hit) ~24 ms
Bulk warm, all 8,725 symbols (12 threads) 61 s, 16,594 revisions
Storage for the full warmed timeline 5.4 MiB inside the graph DB
Shallow clone (--depth 1) git: 1 commit; tool via carried DB: full 21-revision timeline

Since the artifact export compresses the whole DB, warmed timelines ship to
teammates/CI for free — history knowledge becomes as portable as the graph.

With the table warmed, symbol-granularity questions git cannot express
become millisecond SQL over the existing graph: churn × complexity × fan-in
hotspots (69 ms), commits ranked by high-fan-in symbols touched (41 ms),
call-closure × changed-recently audits (16 ms), and cross-file co-change
pairs with no static edge — which on this repo surfaced exactly the
function↔test pairs static edges miss.

Scope of the working implementation

  • store: cbm_node_revision_t + transactional CRUD, lazy schema
  • mcp: get_node_history handler + exported pure cbm_range_map_*
  • tests: 15 passing (store CRUD, drift mapper edge cases)
  • docs: README/CONTRIBUTING tool count + tables
  • No changes to existing schema paths, passes, or tools

Possible follow-ups (separate PRs if there's appetite): opt-in eager warm
pass in BEST mode, find_temporal_coupling / risk_hotspots query tools,
commit→symbols reverse index for regression localization.

Happy to open the PR, split it, or adjust the design.

Alternatives considered

  • Raw git archaeology per query (agent shells out to git log -L): fails on shallow clones, no symbol granularity, and no way to join history with graph topology (co-change, closure audits).
  • Full graph versioning per commit: storage and complexity explode; the lazy sparse cache captures the same value at 5.4 MiB

Confirmations

  • I searched existing issues and this is not a duplicate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions