Skip to content

Incremental Updates & Map-Reduce Caching

To avoid re-analyzing an entire codebase every time a minor file changes, Code-Reducer implements a lightweight, platform-independent caching engine backed by filesystem SHA256 content hashing and state propagation.

Running code-reducer update selectively re-processes only modified source files and affected directory modules, reducing execution time and LLM inference overhead by up to 95% on typical incremental updates.


⚡ Incremental Workflow (code-reducer update)

When code-reducer update is executed, the pipeline performs the following sequence:

sequenceDiagram
    participant User
    participant CLI as update Command
    participant Cache as MetadataCache (.metadata.json)
    participant Engine as Engine / DiscoverCodeFiles
    participant LLM as Ollama Service

    User->>CLI: code-reducer update
    CLI->>Cache: loadMetadataCache()
    CLI->>Engine: computeStepsHash(cfg.ExtractionSteps)
    alt Steps Hash Mismatch
        Engine->>Cache: Invalidate cache (Reset Files & Modules)
    end
    CLI->>Engine: Discover active source files & compute SHA256
    Engine->>Engine: State Classification (Added, Modified, Deleted)
    Engine->>Engine: determineAffected() + propagateAffected() bottom-up
    alt No Affected Directories
        Engine-->>User: "No modifications detected. Documentation is up to date."
    else Affected Directories Exist
        loop For Each Affected Directory Node
            alt File Cache Hit
                Engine->>Cache: Reuse cached facts string
            else File Cache Miss
                Engine->>LLM: Run Extract & Reduce File Facts
                Engine->>Cache: Store SHA256 + Facts
            end
            Engine->>LLM: Synthesize Module README (wiki/modules/<module>.md)
            Engine->>Cache: Store Module Summary
        end
        alt Root Node (.) Affected OR Global Docs Missing
            Engine->>LLM: Generate wiki/architecture.md & wiki/quickstart.md
        end
        CLI->>Cache: saveMetadataCache()
        CLI-->>User: "Pipeline update completed successfully!"
    end

🔑 Hash-Based State Tracking (.metadata.json)

Documentation cache state is persisted inside the designated output folder (e.g. wiki/.metadata.json).

Metadata Cache Schema

{
  "version": 1,
  "steps_hash": "a5d8f7b3c2e1...",
  "files": {
    "internal/engine/orchestrator.go": {
      "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "facts": "#### [API_SIGNATURES]\n..."
    }
  },
  "modules": {
    "internal/engine": "Synthesized module summary markdown content..."
  }
}

Platform-Independent SHA256 Content Hashing

File change detection relies on direct binary content SHA256 computation rather than operating system modification timestamps (mtime) or Git commit history:

func computeSHA256(repoRoot, virtualPath string) (string, error) {
    content, err := tools.ReadFileSafely(repoRoot, virtualPath)
    if err != nil {
        return "", err
    }
    hash := sha256.Sum256(content)
    return hex.EncodeToString(hash[:]), nil
}

This guarantees identical behavior across operating systems (Linux, macOS, Windows) and avoids invalidating caches when files are checked out or touched without content modifications.


📊 File State Classification

During detectFileChanges, active repository files discovered by DiscoverCodeFiles are compared against cache.Files:

Classification Condition Action Taken
Added Present in workspace, missing from cache.Files. Directory containing file marked affected. Full Map extraction triggered for file.
Modified Present in both workspace and cache, but current SHA256 \(\neq\) cached SHA256. Directory marked affected. Fact cache invalidated for file; full extraction re-run.
Deleted Present in cache.Files, missing from active workspace. Immediate parent directory marked affected. Cache entry pruned from cache.Files.

🌲 Bottom-Up Change Propagation

When source files change deep inside a project subfolder, child module changes must reflect in parent module documentation. Code-Reducer achieves this through two-phase affect determination:

1. Direct Affect Evaluation (determineAffected)

A directory node n is directly marked affected if: - Any file in n.Files has a Modified or Added status. - Any file associated with n.Path was Deleted. - The directory's module documentation file (wiki/modules/<module>.md) is physically missing on disk. - The directory entry cache.Modules[n.Path] is missing.

2. Recursive Bottom-Up Propagation (propagateAffected)

Once direct affects are identified, propagateAffected performs a post-order traversal up the tree:

func propagateAffected(node *DirNode, affectedDirs map[string]bool) map[string]bool {
    newAffected := make(map[string]bool)
    for k, v := range affectedDirs {
        newAffected[k] = v
    }

    var propagate func(*DirNode) bool
    propagate = func(n *DirNode) bool {
        isAffected := newAffected[n.Path]
        for _, child := range n.Children {
            if propagate(child) {
                isAffected = true
            }
        }
        if isAffected {
            newAffected[n.Path] = true
        }
        return isAffected
    }
    propagate(node)
    return newAffected
}

If internal/engine/chunking.go changes: 1. internal/engine is marked directly affected. 2. Propagation marks internal affected. 3. Propagation marks . (root) affected. 4. Leaf nodes outside internal (e.g. cmd/) remain unaffected and reuse cached summaries.


🔄 Extraction Steps Cache Invalidation (steps_hash)

If a developer customizes or updates the extraction_steps array in .code-reducer.yaml (e.g. adding a security analysis step or altering extraction prompts), existing cached facts become stale.

Code-Reducer tracks prompt evolution by computing a SHA256 digest of the serialized extraction steps array:

func computeStepsHash(steps []config.ExtractionStep) string {
    data, err := json.Marshal(steps)
    if err != nil {
        return ""
    }
    h := sha256.Sum256(data)
    return hex.EncodeToString(h[:])
}

During code-reducer update: 1. currentStepsHash is calculated from active configuration. 2. If cache.StepsHash != currentStepsHash: - A warning event is logged: "Warning: Extraction steps changed. Invalidating metadata cache to force full documentation regeneration." - cache.Files and cache.Modules are cleared in memory. - cache.StepsHash is updated to currentStepsHash. 3. All files and directories are marked affected, forcing a clean, safe rebuild of the entire documentation suite.