stepchowfun/tagref

Manage cross-references in your code.

Rust

240

585 commits

updated Sep 22, 2026

See the code

README

Tagref

Build status

Welcome to Tagref.

Tagref helps you manage cross-references in your code. You can use it to help keep things in sync, document assumptions, maintain invariants, etc. Airbnb, Notion, and Watershed use it to level up their code health. You can use it too!

Tagref works with any programming language, and it respects your .gitignore file as well as other common filter files. It's recommended to set up Tagref as an automated continuous integration (CI) check. Tagref is fast and almost certainly won't be the bottleneck in your CI.

What is it?

Tagref allows you to annotate your code with tags (in comments) which can be referenced from other parts of the codebase.

Here's an example in Python:

# [tag:polynomial_nonzero] This function never returns zero.
def polynomial(x):
    return x ** 2 + 1

def inverse_polynomial(x):
    return 1 / polynomial(x) # This is safe due to [ref:polynomial_nonzero].

To help you manage these tags and references, Tagref checks the following:

  1. References actually point to tags or groups. A target cannot be deleted or renamed without updating the references that point to it.
  2. Tags are unique. There is never any ambiguity about which tag is being referenced.

When several places are equally responsible for staying in sync, you can use a group instead of choosing one place to hold a tag:

# Keep this representation synchronized with the database schema. [group:user_fields]
class User:
    pass

# Keep this schema synchronized with the application representation. [group:user_fields]
USER_COLUMNS = []

Every group must have at least two members, so deleting or mistyping one member of a two-member group produces an error. A regular reference can point to either a tag or a group, such as [ref:user_fields]. Tags and groups share a label namespace and cannot use the same label.

In the polynomial example, Tagref doesn't guarantee that polynomial returns a nonzero number. It isn't magic! It only ensures that the polynomial_nonzero tag exists unambiguously. The programmer is still responsible for keeping the comments in sync with the code.

In addition to references to tags and groups, Tagref also supports file references and directory references. A file reference guarantees that the given file exists. For example:

# If you bump the version, be sure to update [file:CHANGELOG.md].

A directory reference guarantees that the given directory exists. For example:

# This script will format the files in [dir:src].

By default, file and directory paths are relative to the project root. However, paths that start with a . or .. component (e.g., [file:./CHANGELOG.md]) are considered relative to the directory containing the file where the reference originates.

Labels

The label of a tag or group may consist of any UTF-8 text except the right square bracket ]. Internal whitespace (as in [tag:foo bar]) is allowed, and surrounding whitespace (as in [tag: baz ]) is ignored. Labels are case-sensitive, so [tag:qux] and [tag:Qux] are different tags.

You can use any naming convention you like. The Tagref authors prefer to use lowercase words separated by underscores _, like [tag:important_note].

Usage

The easiest way to use Tagref is to run the tagref command with no arguments. Tagref will look for the nearest tagref.yml in the current directory or one of its ancestors, recursively scan that directory, and check all the tags and references. If no tagref.yml file is found, Tagref will scan the current directory instead.

Here are the supported command-line options:

Usage: tagref [OPTIONS] [COMMAND]

Commands:
  check        Check all the tags and references (default)
  list-tags    List all the tags
  list-groups  List all the group members
  list-refs    List all the references
  list-files   List all the file references
  list-dirs    List all the directory references
  list-unused  List the unreferenced tags
  help         Print this message or the help of the given subcommand(s)

Options:
  -v, --version          Print version
  -c, --config <CONFIG>  Use a config file instead of searching for tagref.yml
  -h, --help             Print help

Configuration

Tagref can be configured by creating a tagref.yml file. This file also tells Tagref where the project root is, such that you can run tagref from anywhere in the project.

Below is an example demonstrating all the supported fields. All fields are optional, so an empty tagref.yml file is valid.

tag_sigil: tag
group_sigil: group
ref_sigil: ref
file_sigil: file
dir_sigil: dir
ignore_rules:
  - /artifacts/
  - /target/

The sigils determine the syntax of tags, groups, and references. For example, if group_sigil is set to sync, then [sync:foo] declares a group member.

The ignore_rules field adds extra ignore rules, interpreted relative to the project root. These rules use the same pattern syntax as .gitignore files. Rules beginning with ! are rejected.

Installation instructions

Installation on macOS or Linux (AArch64 or x86-64)

If you're running macOS or Linux (AArch64 or x86-64), you can install Tagref with this command:

curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | sh

The same command can be used again to update to the latest version.

The installation script supports the following optional environment variables:

  • VERSION=x.y.z (defaults to the latest version)
  • PREFIX=/path/to/install (defaults to /usr/local/bin)

For example, the following will install Tagref into the working directory:

curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | PREFIX=. sh

If you prefer not to use this installation method, you can download the binary from the releases page, make it executable (e.g., with chmod), and place it in some directory in your PATH (e.g., /usr/local/bin).

Installation on Windows (AArch64 or x86-64)

If you're running Windows (AArch64 or x86-64), download the latest binary from the releases page and rename it to tagref (or tagref.exe if you have file extensions visible). Create a directory called Tagref in your %PROGRAMFILES% directory (e.g., C:\Program Files\Tagref), and place the renamed binary in there. Then, in the "Advanced" tab of the "System Properties" section of Control Panel, click on "Environment Variables..." and add the full path to the new Tagref directory to the PATH variable under "System variables". Note that the Program Files directory might have a different name if Windows is configured for a language other than English.

To update an existing installation, simply replace the existing binary.

Installation with Homebrew

If you have Homebrew, you can install Tagref as follows:

brew install tagref

You can update an existing installation with brew upgrade tagref.

Installation with Cargo

If you have Cargo, you can install Tagref as follows:

cargo install tagref

You can run that command with --force to update an existing installation.

Installation with pre-commit

If you use pre-commit, you can install Tagref by adding it to your .pre-commit-config.yaml as follows:

repos:
- repo: https://github.com/stepchowfun/tagref
  rev: v1.14.0
  hooks:
  - id: tagref

If you happen to have Rust installed, make sure it's up-to-date since pre-commit will use it to install Tagref. If you don't already have Rust, pre-commit will install it for you.

Editor integrations

  • tagref.el: An Emacs minor mode with tag/reference completion, xref-based navigation, and validation support.

Acknowledgements

The idea for Tagref was inspired by the GHC notes system described in this article (§5.6).

continuous-integration
cross-reference
cross-references
cross-referencing
linter

Contributors

stepchowfun

568 commits

naiquevin

4 commits

vedang

4 commits

epilys

3 commits

stepchowfun/tagref

Manage cross-references in your code.

Rust

240

585 commits

updated Sep 22, 2026

See the code

README

Tagref

Build status

Welcome to Tagref.

Tagref helps you manage cross-references in your code. You can use it to help keep things in sync, document assumptions, maintain invariants, etc. Airbnb, Notion, and Watershed use it to level up their code health. You can use it too!

Tagref works with any programming language, and it respects your .gitignore file as well as other common filter files. It's recommended to set up Tagref as an automated continuous integration (CI) check. Tagref is fast and almost certainly won't be the bottleneck in your CI.

What is it?

Tagref allows you to annotate your code with tags (in comments) which can be referenced from other parts of the codebase.

Here's an example in Python:

# [tag:polynomial_nonzero] This function never returns zero.
def polynomial(x):
    return x ** 2 + 1

def inverse_polynomial(x):
    return 1 / polynomial(x) # This is safe due to [ref:polynomial_nonzero].

To help you manage these tags and references, Tagref checks the following:

  1. References actually point to tags or groups. A target cannot be deleted or renamed without updating the references that point to it.
  2. Tags are unique. There is never any ambiguity about which tag is being referenced.

When several places are equally responsible for staying in sync, you can use a group instead of choosing one place to hold a tag:

# Keep this representation synchronized with the database schema. [group:user_fields]
class User:
    pass

# Keep this schema synchronized with the application representation. [group:user_fields]
USER_COLUMNS = []

Every group must have at least two members, so deleting or mistyping one member of a two-member group produces an error. A regular reference can point to either a tag or a group, such as [ref:user_fields]. Tags and groups share a label namespace and cannot use the same label.

In the polynomial example, Tagref doesn't guarantee that polynomial returns a nonzero number. It isn't magic! It only ensures that the polynomial_nonzero tag exists unambiguously. The programmer is still responsible for keeping the comments in sync with the code.

In addition to references to tags and groups, Tagref also supports file references and directory references. A file reference guarantees that the given file exists. For example:

# If you bump the version, be sure to update [file:CHANGELOG.md].

A directory reference guarantees that the given directory exists. For example:

# This script will format the files in [dir:src].

By default, file and directory paths are relative to the project root. However, paths that start with a . or .. component (e.g., [file:./CHANGELOG.md]) are considered relative to the directory containing the file where the reference originates.

Labels

The label of a tag or group may consist of any UTF-8 text except the right square bracket ]. Internal whitespace (as in [tag:foo bar]) is allowed, and surrounding whitespace (as in [tag: baz ]) is ignored. Labels are case-sensitive, so [tag:qux] and [tag:Qux] are different tags.

You can use any naming convention you like. The Tagref authors prefer to use lowercase words separated by underscores _, like [tag:important_note].

Usage

The easiest way to use Tagref is to run the tagref command with no arguments. Tagref will look for the nearest tagref.yml in the current directory or one of its ancestors, recursively scan that directory, and check all the tags and references. If no tagref.yml file is found, Tagref will scan the current directory instead.

Here are the supported command-line options:

Usage: tagref [OPTIONS] [COMMAND]

Commands:
  check        Check all the tags and references (default)
  list-tags    List all the tags
  list-groups  List all the group members
  list-refs    List all the references
  list-files   List all the file references
  list-dirs    List all the directory references
  list-unused  List the unreferenced tags
  help         Print this message or the help of the given subcommand(s)

Options:
  -v, --version          Print version
  -c, --config <CONFIG>  Use a config file instead of searching for tagref.yml
  -h, --help             Print help

Configuration

Tagref can be configured by creating a tagref.yml file. This file also tells Tagref where the project root is, such that you can run tagref from anywhere in the project.

Below is an example demonstrating all the supported fields. All fields are optional, so an empty tagref.yml file is valid.

tag_sigil: tag
group_sigil: group
ref_sigil: ref
file_sigil: file
dir_sigil: dir
ignore_rules:
  - /artifacts/
  - /target/

The sigils determine the syntax of tags, groups, and references. For example, if group_sigil is set to sync, then [sync:foo] declares a group member.

The ignore_rules field adds extra ignore rules, interpreted relative to the project root. These rules use the same pattern syntax as .gitignore files. Rules beginning with ! are rejected.

Installation instructions

Installation on macOS or Linux (AArch64 or x86-64)

If you're running macOS or Linux (AArch64 or x86-64), you can install Tagref with this command:

curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | sh

The same command can be used again to update to the latest version.

The installation script supports the following optional environment variables:

  • VERSION=x.y.z (defaults to the latest version)
  • PREFIX=/path/to/install (defaults to /usr/local/bin)

For example, the following will install Tagref into the working directory:

curl https://raw.githubusercontent.com/stepchowfun/tagref/main/install.sh -LSfs | PREFIX=. sh

If you prefer not to use this installation method, you can download the binary from the releases page, make it executable (e.g., with chmod), and place it in some directory in your PATH (e.g., /usr/local/bin).

Installation on Windows (AArch64 or x86-64)

If you're running Windows (AArch64 or x86-64), download the latest binary from the releases page and rename it to tagref (or tagref.exe if you have file extensions visible). Create a directory called Tagref in your %PROGRAMFILES% directory (e.g., C:\Program Files\Tagref), and place the renamed binary in there. Then, in the "Advanced" tab of the "System Properties" section of Control Panel, click on "Environment Variables..." and add the full path to the new Tagref directory to the PATH variable under "System variables". Note that the Program Files directory might have a different name if Windows is configured for a language other than English.

To update an existing installation, simply replace the existing binary.

Installation with Homebrew

If you have Homebrew, you can install Tagref as follows:

brew install tagref

You can update an existing installation with brew upgrade tagref.

Installation with Cargo

If you have Cargo, you can install Tagref as follows:

cargo install tagref

You can run that command with --force to update an existing installation.

Installation with pre-commit

If you use pre-commit, you can install Tagref by adding it to your .pre-commit-config.yaml as follows:

repos:
- repo: https://github.com/stepchowfun/tagref
  rev: v1.14.0
  hooks:
  - id: tagref

If you happen to have Rust installed, make sure it's up-to-date since pre-commit will use it to install Tagref. If you don't already have Rust, pre-commit will install it for you.

Editor integrations

  • tagref.el: An Emacs minor mode with tag/reference completion, xref-based navigation, and validation support.

Acknowledgements

The idea for Tagref was inspired by the GHC notes system described in this article (§5.6).

continuous-integration
cross-reference
cross-references
cross-referencing
linter

Contributors

stepchowfun

568 commits

naiquevin

4 commits

vedang

4 commits

epilys

3 commits

Languages

Rust

94.6%

Shell

5.4%