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 callref(), you get back a RefResult object with several useful properties beyond .content:
| Property | Type | Description |
|---|---|---|
content | str | The compiled document output |
name | str | Document name from frontmatter (or filename) |
description | str | None | Description from frontmatter |
template | str | Raw source template before compilation |
updated | datetime | When the document was last compiled |
uri | str | The document’s URI |
source | object | None | Original domain object (for provider resources) |
Building Document Indexes
Thename and description properties are particularly useful for creating navigation or summary documents:
models/index.md
Accessing Raw Templates
Thetemplate property gives you the uncompiled source, useful when you want to show examples or analyze document structure:
models/style-guide.md
Graph Construction
Colin builds the dependency graph through a two-pass process. First, it parses all templates using Jinja’s AST to findref() calls. Second, it uses these calls to construct directed edges from dependent documents to their dependencies.
Consider this set of documents:
models/company.md
models/products/overview.md
models/sales/pitch.md
products/overview depends on company, and sales/pitch depends on both products/overview and company. The resulting graph:
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’srefs_evaluated list, recording which documents were actually referenced during the last compilation. This list drives propagation decisions.
Change Detection
Colin detects changes by comparing hashes:- Source hash - Has the template file itself changed?
- Output hash of dependencies - Has any referenced document’s output changed?
company.md triggers recompilation of products/overview.md because its dependency changed, even though its own source file remains unchanged.
Downstream Closure
When you runcolin 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→ recompilecompany,products/overview,sales/pitch - Change
products/overview.md→ recompileproducts/overview,sales/pitch - Change
sales/pitch.md→ recompile onlysales/pitch
Practical Patterns
Layered Context
A common pattern separates raw sources from synthesized context from final outputs:Shared Components
Extract common content into shared documents that multiple outputs reference:models/shared/disclaimer.md
models/reports/weekly.md
models/reports/monthly.md
Conditional References
Jinja control flow can make references conditional, but Colin still detects allref() calls during AST parsing. The dependency edge exists even if the branch doesn’t execute at runtime:
models/report.md
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 useref() with a provider object, Colin records it in refs_evaluated:
models/context/project-status.md