Skip to main content
The ref() function does more than fetch content. It builds a dependency graph that Colin uses to compile documents in the correct order and recompile only what changes. Every ref() call registers an edge in this graph, connecting the current document to the referenced one. This dependency tracking is Colin’s core mechanism for incremental compilation. When you modify a document, Colin traces all documents that depend on it and recompiles them automatically. The graph ensures that parent documents always compile before their dependents.

RefResult Properties

When you call ref(), you get back a RefResult object with several useful properties beyond .content:
PropertyTypeDescription
contentstrThe compiled document output
namestrDocument name from frontmatter (or filename)
descriptionstr | NoneDescription from frontmatter
templatestrRaw source template before compilation
updateddatetimeWhen the document was last compiled
uristrThe document’s URI
sourceobject | NoneOriginal domain object (for provider resources)
These properties enable sophisticated document processing. You can build indexes, generate summaries, or make decisions based on metadata without parsing content yourself.

Building Document Indexes

The name and description properties are particularly useful for creating navigation or summary documents:
models/index.md
---
name: Knowledge Base Index
description: Auto-generated index of all context documents
---

# Available Context

{% for doc_uri in ['company', 'products/overview', 'team/structure'] %}
{% set doc = ref(doc_uri) %}
## {{ doc.name }}

{{ doc.description or 'No description provided.' }}

*Last updated: {{ doc.updated.strftime('%Y-%m-%d') }}*

---
{% endfor %}
This produces a formatted index with names, descriptions, and timestamps from each referenced document.

Accessing Raw Templates

The template property gives you the uncompiled source, useful when you want to show examples or analyze document structure:
models/style-guide.md
---
name: Documentation Style Guide
---

# Writing Standards

Here's an example of how we structure product documents:

{{ ref('products/widget').template }}

Notice how the template uses ref() to pull in company context...

Graph Construction

Colin builds the dependency graph through a two-pass process. First, it parses all templates using Jinja’s AST to find ref() calls. Second, it uses these calls to construct directed edges from dependent documents to their dependencies. Consider this set of documents:
models/company.md
---
name: Company Overview
---

Acme Corp builds developer tools for CI/CD pipelines.
models/products/overview.md
---
name: Product Overview
---

{{ ref('company').content }}

## Our Products

We offer three main product lines...
models/sales/pitch.md
---
name: Sales Pitch
---

{{ ref('products/overview').content }}

## Why Choose Us

{{ ref('company').content }}
Colin sees that products/overview depends on company, and sales/pitch depends on both products/overview and company. The resulting graph:
company

   ├──────────────────┐
   ▼                  ▼
products/overview   sales/pitch
   │                  ▲
   └──────────────────┘
Colin uses topological sorting to determine compilation order. Documents with no dependencies compile first, then documents that depend only on completed documents, and so on. Within each level, documents compile in parallel.

Dependency Propagation

When a document changes, Colin traces its dependents through the graph and marks them for recompilation. This propagation is transitive: if A depends on B and B depends on C, changing C triggers recompilation of both B and A. The manifest tracks each document’s refs_evaluated list, recording which documents were actually referenced during the last compilation. This list drives propagation decisions.
{
  "documents": {
    "sales/pitch": {
      "refs_evaluated": ["products/overview", "company"],
      "source_hash": "abc123",
      "output_hash": "def456"
    }
  }
}

Change Detection

Colin detects changes by comparing hashes:
  1. Source hash - Has the template file itself changed?
  2. Output hash of dependencies - Has any referenced document’s output changed?
If either condition is true, the document recompiles. This means that editing company.md triggers recompilation of products/overview.md because its dependency changed, even though its own source file remains unchanged.

Downstream Closure

When you run colin run, Colin computes the “downstream closure” of all changed documents. Starting from each changed document, it walks the graph to find every document that transitively depends on the changed ones. For the example graph above:
  • Change company.md → recompile company, products/overview, sales/pitch
  • Change products/overview.md → recompile products/overview, sales/pitch
  • Change sales/pitch.md → recompile only sales/pitch

Practical Patterns

Layered Context

A common pattern separates raw sources from synthesized context from final outputs:
models/
├── sources/           # Raw data, MCP fetches
│   ├── linear.md
│   └── calls.md
├── context/           # Synthesized information
│   ├── project-health.md
│   └── customer-sentiment.md
└── skills/            # Final agent skills
    └── support-agent.md
Sources feed context documents, context documents feed skills. Changes flow downstream through this hierarchy.

Shared Components

Extract common content into shared documents that multiple outputs reference:
models/shared/disclaimer.md
---
name: Legal Disclaimer
---

*This information is provided for internal use only and should not be shared externally without approval.*
models/reports/weekly.md
---
name: Weekly Report
---

# Weekly Status

{{ ref('context/project-health').content }}

{{ ref('shared/disclaimer').content }}
models/reports/monthly.md
---
name: Monthly Summary
---

# Monthly Summary

{{ ref('context/project-health').content }}
{{ ref('context/customer-sentiment').content }}

{{ ref('shared/disclaimer').content }}
Updating the disclaimer automatically updates both reports on the next compile.

Conditional References

Jinja control flow can make references conditional, but Colin still detects all ref() calls during AST parsing. The dependency edge exists even if the branch doesn’t execute at runtime:
models/report.md
---
name: Conditional Report
---

{% if include_financials %}
{{ ref('context/financials').content }}
{% endif %}

{{ ref('context/operations').content }}
Both context/financials and context/operations appear in the dependency graph regardless of the include_financials value. This ensures Colin can correctly order compilation even when conditional branches might activate in future compiles.

External Dependencies

Colin tracks dependencies from external sources like MCP servers alongside local documents. When you use ref() with a provider object, Colin records it in refs_evaluated:
models/context/project-status.md
---
name: Project Status
---

{# MCP resource tracked as a dependency #}
{{ ref(colin.mcp.linear.resource('projects?team=engineering')).content }}

{# Local document also tracked #}
{{ ref('context/team-members').content }}
The manifest records both types of dependencies:
{
  "documents": {
    "context/project-status": {
      "refs_evaluated": [
        "mcp.linear://?resource=projects%3Fteam%3Dengineering",
        "project://context/team-members.md"
      ]
    }
  }
}
This unified tracking means changes to any dependency—local or external—trigger recompilation of dependent documents.