Do you need to install anything?
Three kinds of people arrive here, and only one of them needs the install that follows.
Nothing to install. table.iddn.uk opens a graph straight from GitHub in your browser, shows you each item that is waiting for a signature, and lets you ratify or reject it there. You sign in with GitHub and nothing is put on your computer.
If you have never used a command line, stop here and use that.
Your own machine needs nothing. The skills tell Claude how to work inside a graph, and they tell it to install throughline itself, where it runs, if the tool is not already there. A session on the web runs in its own container; a local session installs onto the machine Claude is running on.
You will still sign what Claude proposes: at table.iddn.uk, or with tl-ratify after the install below.
You will author graphs on your own machine, or run the check as a gate in CI. Then yes: read on.
Install
throughline is a small Python package with one dependency and needs Python 3.11 or later. The command it puts on your path is tl. Whatever you run, the install is one line; the routes below are about getting pipx, which keeps a command-line tool in its own environment so it cannot disturb anything else on the machine.
pipx install throughline
tl --version
If you already use uv, uv tool install throughline does the same job and brings its own Python.
macOS
With Homebrew:
brew install python pipx
pipx ensurepath
pipx install throughline
Open a new terminal window after ensurepath so the shell picks up the path it added. Without Homebrew, install Python 3.12 from python.org, then python3 -m pip install --user pipx and python3 -m pipx ensurepath, and carry on from the third line.
Windows
Install Python first, either from python.org, ticking Add python.exe to PATH, or from a terminal:
winget install Python.Python.3.12
Then, in a new terminal:
python -m pip install --user pipx
python -m pipx ensurepath
pipx install throughline
Open another new terminal before the last line, so that pipx is found. You get tl.exe, so tl works in PowerShell and in the Command Prompt alike.
If you will be signing with tl-ratify, it installs the same way, pipx install throughline-ratify, and from 0.12.5 brings the curses it draws with, which Python on Windows does not include. An older install that fails with No module named '_curses' is repaired by pipx upgrade throughline-ratify.
If you would rather work in a Linux shell on Windows, with the same commands as every Linux page you will read, use WSL2 instead.
Windows, with WSL2
WSL2 runs Ubuntu inside Windows. If you do not have it yet, from an administrator terminal:
wsl --install
Restart when it asks, open Ubuntu from the Start menu, and follow the Debian and Ubuntu route below. Keep your repositories inside the Linux file system, under your Ubuntu home directory, not under /mnt/c: Git and tl check are many times faster there, and Windows tools can still reach the files through \\wsl$ in Explorer.
Linux
Do not pip install into the system Python. On current distributions it is marked externally managed and will refuse; on older ones it will accept and later break something that depended on it. Get pipx from the distribution and let it keep throughline apart.
Debian and Ubuntu. Debian 12 and Ubuntu 23.04 or later ship a new enough Python. Ubuntu 22.04 ships Python 3.10, which is too old, and so does Debian 11; on those, install a newer Python first and pass it to pipx with pipx install --python python3.12 throughline, or use the container route and leave the system alone.
sudo apt install pipx
pipx ensurepath
pipx install throughline
Fedora.
sudo dnf install pipx
pipx ensurepath
pipx install throughline
Arch.
sudo pacman -S python-pipx
pipx ensurepath
pipx install throughline
Anything else with Python 3.11 or later but no pipx package:
python3 -m pip install --user pipx
python3 -m pipx ensurepath
pipx install throughline
Open a new shell after ensurepath on every route above; it edits your shell profile and the shell you are in has not read it.
Docker, with no Python on the host
There is no published image, and you do not need one: the tool installs from the package index inside a stock Python image. Put these four lines in a file called Dockerfile:
FROM python:3.12-slim
RUN pip install --no-cache-dir throughline
WORKDIR /work
ENTRYPOINT ["tl"]
Build it once, then run it against any project by mounting the project at /work:
docker build -t throughline .
docker run --rm -v "$PWD:/work" throughline check --strict
Everything after the image name is passed to tl, so init, context and the rest work the same way. This is also the shape of a CI gate: the same image, the same line, on every push.
Check it worked
tl --version
prints tl and a version. If the shell says it cannot find tl, the path ensurepath added has not been read yet: open a new terminal. Nearly every failed install is one of two things: a Python older than 3.11, which python3 --version answers, or a terminal opened before ensurepath ran. Still stuck: open an issue with your system and the line that failed.
If you will be the one signing, pipx install throughline-ratify adds tl-ratify, the assistant that shows you each proposed item before you sign it, one at a time.
Start a graph
tl init seeds a small, self-consistent example so you begin from a green graph rather than a blank folder:
tl init --name "acme-app"
tl check --strict # 0 error(s), 0 warning(s) → exit 0
tl trace REQ-0001 # walk the requirement up to its reason
You get an intent, a requirement that implements it, a quality attribute, a test that verifies the requirement, a non-goal, and a rendered document whose generated regions the tool keeps in step. Rename, move or delete any of it; tl init --bare writes the configuration alone.
Build one by hand, and watch it fail first
The fastest way to feel why the grounding layer matters is to create items ungrounded and let the check reject them:
tl init --name "acme-app" --bare
tl register new INT vision --title "Product vision"
tl register new BN goals --title "Business goals"
tl register new FR features --title "Functional requirements"
tl new INT --type intent --title "Ship value to users every week"
tl new BN --type business_need --title "Cut new-user onboarding time in half"
tl new FR --type requirement --title "Guided 3-step setup wizard" \
--text "The system shall walk a new user through setup in 3 steps."
tl check --strict
4 error(s), 0 warning(s)
[ERROR] BN-0001 unserved-root business_need has nothing deriving from / mitigating it
[ERROR] FR-0001 orphan requirement has no grounding link — nothing justifies it
[ERROR] FR-0001 coverage needs incoming 'verifies' link
[ERROR] INT-0001 unserved-root intent has nothing deriving from / mitigating it
The feature justifies nothing and the roots serve nothing. Add the links, and a test:
tl link BN-0001 INT-0001 --type derives_from
tl link FR-0001 BN-0001 --type derives_from
tl register new TEST tests --title "Verification"
tl new TEST --type test --title "Wizard completes in 3 steps" --ground FR-0001 --ground-type verifies
tl check --strict # 0 error(s) → exit 0
Better, ground at birth: tl new FR … --ground BN-0001 creates the item already justified, so an ungrounded graph is something you have to opt into rather than remember to fix. On a terminal with no --ground, the tool offers the existing roots as a picker.
Bring an AI in
Give an agent tl context first. It prints a brief generated from the project's live configuration: the discipline, the item types and their attributes, the link vocabulary, the statuses and the moves between them. An agent that creates items with --origin ai puts them in proposed, where the strict check holds them as errors until a person signs.
Signing is a person's act, done reading. tl ratify records a name against the exact wording; the companion tl-ratify is a full-screen assistant that shows each proposed item in turn, with what changed since any earlier signature, and asks. Withdraw a signature that should not stand with tl withdraw; flag an ambiguous wording with tl flag, which blocks ratification until tl clarify records who judged it resolved.
tl context # the brief an agent reads first
tl new REQ --origin ai --ground INT-0001 … # born proposed
tl-ratify --by "Your Name" # read each one; sign or send back
For Claude Code there is a plugin marketplace at skills.iddn.uk with skills that shape a new graph by a discovery conversation and run a challenge pass over proposed items before they reach a person.
Gate it in CI
The exit codes are a contract, so the check drops straight into a pipeline or a pre-commit hook. One step is enough:
- run: pipx install throughline
- run: tl -C idd check --strict
- run: tl -C idd docs --check # if you publish documents from the graph
From then on an ungrounded requirement, an unsigned AI item, a stale signature or a drifted document fails the build, and the review queue is bounded by what the graph will accept rather than by what a model will generate.
Compose a standard
To adopt a published standard by reference, add a source to throughline.toml and cite its clauses by namespace:
[[sources]]
namespace = "asvs"
url = "https://github.com/rhodium-org/throughline-asvs"
ref = "v5.0.0"
tl link REQ-0012 asvs:SR-0133 --type satisfies
The catalogue lists every public source and gives the block to paste for each. The check composes them into your graph and reports in the source's own vocabulary; your project's statuses, attributes and rules stay yours.
Read and sign in a browser
Not everyone who must sign has a terminal. tools.iddn.uk gathers the browser tools: the table puts a graph in front of people to read, sign and take away as a document; explore takes one apart and measures it; sync keeps a folder on a computer in step with a branch on GitHub without git installed. Each runs in the browser and talks to GitHub alone.
Keep it honest
- Do only work the graph justifies. If nothing calls for it, author the intent and have a person sign it first.
- Change the graph through the command line, never by editing an item file's structure by hand: the tool is what keeps identifiers unique, links typed and moves legal.
- Write an item short, and rewrite rather than append; a requirement nobody finishes reading does not bind.
- If a requirement binds someone outside the project, say in its rationale who pays and what it costs them.