Typed-relationship annotation layer over content, layered on git
Project description
README
Tether is an agent-first, semantic relationship harness extension. It consists of simple database, hooks for agents, and a CLI interface.
The core object is a tether. A tether is a link between two artifacts (e.g. a function and a markdown section) that defines a relationship between the artifacts. This is easily understood when viewing the data model:
The Tether
{
"a": {
"fingerprint": {
"file_blob_oid": "a3a02...",
"region_hash": "9d2e0..."
},
"locator": { "kind": "symbol", "lang": "python", "selector": "reset_password" },
"path": "src/auth.py"
},
"b": {
"fingerprint": {
"file_blob_oid": "e417b...",
"region_hash": "1c5f8..."
},
"locator": { "kind": "heading", "lang": "markdown", "selector": "Auth/Password reset" },
"path": "docs/auth.md"
},
"description": "The Password reset section of docs/auth.md specifies the reset_password function in src/auth.py."
}
[!NOTE] Bookkeeping fields (
id,schema_version,created_at,refreshed_at) are omitted here to keep the example focused.
As seen above, a tether consists of two artifacts, a and b, plus a description. Each artifact has a path and a content fingerprint; when it targets a region of a file rather than the whole thing, it also carries a locator naming that region — here the reset_password function and the Password reset section. The description is what makes the connection a semantic one.
Concepts
| Term | Meaning |
|---|---|
| Tether | A declaration of connection between two artifacts, with content fingerprints recorded at both ends and a required description. |
| Artifact | One end of a tether: a path, optionally narrowed to a region via a locator. A whole-file artifact carries no locator. |
| Region | A locator-addressed part of a file — a Python symbol (auth.py::reset_password) or a markdown heading (auth.md::Auth/Password reset). A region tether drifts only when that part changes, not the rest of the file. |
| Fingerprint | The git blob OID of the artifact's content, captured at tether creation or refresh. |
| Drift | The condition where current content no longer matches the fingerprint. |
| HEALTHY / DRIFTED / BROKEN | Per-artifact state. A tether's aggregate is the most severe of its two artifacts. |
Full glossary: DICTION.md (MVP vocabulary, matches current code).
Relationship Layer
If Git is the content layer, then Tether is the relationship layer.
Tether is built on top of Git, though any VCS would likely suffice, and as such, it relies on, and exploits, the many advantages of having a version-control.
Furthermore, Tether uses a simple database to store tethers, which should be included in git commits.
Requirements
- Python 3.11+
- A git repository. Tether records fingerprints as git blob OIDs, so
tether initrefuses to run outside a git work tree.
Install
Install it as a standalone tool:
uv tool install tether-it # with uv
# or
pipx install tether-it # with pipx
…or add it to a project's environment:
uv add tether-it
Confirm it resolves:
tether --help
Quick start
cd my-project
tether init # create .tether/ in the repo
# Link two files whose content must stay aligned:
tether add docs/auth.md src/auth.py \
--description "The auth.md specifies password reset and 2FA enrollment implemented in the auth module."
tether show
Claude Code
Tether is agent-first: it ships a Claude Code integration that keeps a coding agent aware of drift as it works.
tether init claude-code
This installs, in the current project:
- a memory fragment (
.tether/tether.md, imported intoCLAUDE.md) teaching the agent the tether vocabulary and how to react to drift; - SessionStart and Stop hooks that surface drifted or broken tethers at the start of a session and when the agent finishes a turn;
- permission rules that route the agent through the
tetherCLI — blocking hand-edits under.tether/and pre-approving the read-only subcommands; - a
/tether-onboardskill for seeding a project's initial tether graph.
Seed the graph with /tether-onboard
In a project that has no tethers yet, invoke the skill from Claude Code:
/tether-onboard
The agent surveys the repository, proposes candidate relationships (doc↔code, test↔code, schema↔consumer, registry↔variants, and more), judges each one with precision over recall, records the keepers via tether add with quality descriptions, and reports coverage at the end. It runs autonomously and only when you invoke it — it never triggers on its own.
From then on, when the agent reads a tethered file, tether injects that file's relationships and their drift state alongside the content, so coordinated edits happen within the turn.
CLI reference
| Command | Purpose |
|---|---|
tether init |
Initialize .tether/ in the current git repo. |
tether init claude-code |
Install the Claude Code integration (hooks, permissions, memory fragment, onboard skill). |
tether add A B --description "…" |
Create a tether. A/B may carry a ::selector suffix to tether a region rather than the whole file (e.g. src/calc.py::Calculator.multiply). --description is required. |
tether status [ID] [--json] [--plain] [--no-color] [--diff/--no-diff] |
Report tether state; per-artifact view + diff for one ID, summary for all. |
tether show [--plain] [--no-color] |
List every tether with its description, regardless of state. |
tether refs PATH [--json] [--plain] [--no-color] |
List tethers referencing a path. |
tether coverage [--list-untethered-files] [--list-tethered-files] [--json] |
Report what fraction of git-tracked files participate in a tether. |
tether refresh ID |
Re-fingerprint both artifacts; assert they are aligned. |
tether update ID [--a-path P] [--b-path P] [--a-selector S] [--b-selector S] [--description …] |
Change a path, region selector, or description without touching fingerprints. |
tether mv OLD NEW |
Rewrite every tether artifact pointing at OLD to NEW. |
tether rm ID |
Delete a tether record. |
status, show, and refs adapt to where their output goes: on a terminal they render a colorized, aligned view; piped or run by an agent they emit plain text (status, show) or JSON (refs). Force a surface with --json / --plain, or keep the layout without color via --no-color.
How drift works
tether status resolves each artifact against the current on-disk bytes and compares its git blob OID to the recorded fingerprint:
- HEALTHY — the OID matches.
- DRIFTED — the file exists but its content's OID differs.
- BROKEN — the file is no longer at the recorded path; tether feeds the path and fingerprint to git's rename detector and surfaces the most content-similar file as a rename candidate.
When the raw OIDs disagree, tether re-runs the comparison through a language-agnostic normalizer (line endings, BOM, trailing whitespace, EOF newlines, leading-tab expansion). If the normalized content matches, the artifact stays HEALTHY and the difference is reported as encoding-only.
For a region tether (an artifact with a locator), state is scoped to the selected part of the file: it is DRIFTED only when that symbol or section changes — edits elsewhere in the file leave it HEALTHY — and BROKEN if the file is gone or the locator no longer resolves (the symbol or heading was renamed or removed).
Storage & version control
State lives as one JSON file per tether under .tether/tethers/<uuid7>.json (sorted-key, pretty-printed), committed alongside your content. The graph is reviewable in pull requests and travels with the repo, and git log .tether/tethers/<id>.json is a tether's audit trail — when it was created, refreshed, and retargeted.
Documentation
- Glossary (
DICTION.md) — the canonical vocabulary.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tether_it-0.2.0.tar.gz.
File metadata
- Download URL: tether_it-0.2.0.tar.gz
- Upload date:
- Size: 492.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a9a7a03a1742dbcde73569e0de8a35ec7fb25b38fda8859b3785282785a04f3
|
|
| MD5 |
91e43df086e9ec878ab9e6d1c1162937
|
|
| BLAKE2b-256 |
bbc55e1edb8de88b615d0b429403dc66bbc1cc175a633f879e2bb77edafaab47
|
Provenance
The following attestation bundles were made for tether_it-0.2.0.tar.gz:
Publisher:
publish.yml on rootdrew27/tether
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tether_it-0.2.0.tar.gz -
Subject digest:
9a9a7a03a1742dbcde73569e0de8a35ec7fb25b38fda8859b3785282785a04f3 - Sigstore transparency entry: 1855334456
- Sigstore integration time:
-
Permalink:
rootdrew27/tether@1594152fd02df3e8f9109662ece1d4b17bbeaef1 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/rootdrew27
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1594152fd02df3e8f9109662ece1d4b17bbeaef1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tether_it-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tether_it-0.2.0-py3-none-any.whl
- Upload date:
- Size: 54.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a0a74bbe0ffe0569ea295a7846ebf0b10601ad2b3cd59eecfbced463cac4d24
|
|
| MD5 |
a0bb437a00eedfc61c04220ecf200103
|
|
| BLAKE2b-256 |
e65b9b0cb98dda79c085995efe406240d2ff0b035c97600013845cfb7cbcc1b5
|
Provenance
The following attestation bundles were made for tether_it-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on rootdrew27/tether
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tether_it-0.2.0-py3-none-any.whl -
Subject digest:
7a0a74bbe0ffe0569ea295a7846ebf0b10601ad2b3cd59eecfbced463cac4d24 - Sigstore transparency entry: 1855334526
- Sigstore integration time:
-
Permalink:
rootdrew27/tether@1594152fd02df3e8f9109662ece1d4b17bbeaef1 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/rootdrew27
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1594152fd02df3e8f9109662ece1d4b17bbeaef1 -
Trigger Event:
push
-
Statement type: