01. Why a leash
A coding agent with a rules file, a few subagents and a plan gets a lot done. I have shipped an Nx migration, a UI-kit port and dozens of features that way. What a rules file cannot do is guarantee anything. An instruction is a request. Under a long context or a confusing tool result, the agent forgets it, reinterprets it, or follows an instruction it read in a ticket instead.
Before writing any code I scored my own setup against a 50-item rubric. I built it from public guidance: NIST's AI risk framework and its generative-AI profile, the OWASP lists for LLM and agentic applications, the OpenTelemetry conventions for AI systems, and the agent guidance published by Anthropic, OpenAI, GitHub and Microsoft. The result was humbling:
| Criteria | Count |
|---|---|
| Met | 16 |
| Partly met | 18 |
| Gap | 15 |
The top gaps were the ones you would guess. There was no telemetry, so my token ledger was typed by hand. There was no test set for the harness itself. Several MCP tools could write to external systems unchecked. Nothing said that text from a ticket is data, not instructions. tether is the fix for those gaps, packaged so it travels to every repository I work in.
If a rule can be a script, it is a script. tether is the scripts.
02. Where tether sits
Every guard is a Claude Code hook: a small Node script on a tool event. The diagram shows where each one fires in an agent's loop.
.claude/harness.json, and in a repository without that file every hook does nothing.03. Guards that cannot be talked out of
| Guard | Stops | Why it exists |
|---|---|---|
| git guard | a commit or push on a protected branch; a commit that names a local-only file | a shared checkout once had the wrong branch reset, and private harness files must never reach a PR |
| MCP write guard | write tools of configured servers (design tools, trackers, code-quality services), by exact name or pattern, including claude.ai connectors | a shell deny list does not cover MCP tool names; the guard is mirrored into permissions.deny as a second layer |
| untrusted-input | nothing; it marks every result from a ticket, PR thread, design file or web page as data | prompt injection arrives through exactly those channels |
| report check | an implementer that stops without Files:, Checks:, Deferred: and Plan edits: | manual checks used to be skipped silently |
| fix-round cap | a task that needs more than two fix rounds | past two rounds, an agent rarely converges; escalate to a human instead |
| spec word cap | a spec over the configured length | an uncapped spec once ran to 16,800 words |
Every hook fails open on bad input and stays inactive without a config file. A guard
that breaks your session on a malformed payload gets switched off by Friday. Each hook also has a
node:test spec for its allow, block, missing-config and malformed-input cases.
A plugin cannot set permissions, environment variables, the sandbox or telemetry. Those belong to the user's settings, by design.
/tether:setup computes the change, prints a diff of key names only (never values), waits for a yes, backs the file up and writes it atomically. A second run prints "no changes". The plugin never edits your settings on its own.
04. A human at the right gates
"Human in the loop" is easy to say and hard to place. If you ask for approval everywhere, people click yes without reading. If you ask nowhere, you get an agent that publishes. The workflow tether supports puts a person at five gates and lets scripts handle the rest:
- The design. The human approves the brainstormed approach before a spec is written.
- The spec. The human reads it before a plan is made.
- Settings and anything outward-facing. Writing settings, pulling an image, a reviewer policy, a push or a publish: the agent prepares it and the human runs it.
- Escalations. The fix-round cap hands a stuck task back with the failing check.
- Verification. The run report ends with a sign-off table: SHA, date, who verified it, and the deferred manual checks.
Between those gates, hooks and tests do the checking. Several commands are marked as user-only, so the model cannot start a telemetry stack or an expensive eval run on its own.
05. Seeing what the agents did
Claude Code can export OpenTelemetry: tokens and cost by agent and model, tool results, permission and
hook decisions, and per-subagent traces. tether ships a grafana/otel-lgtm stack pinned by
digest, bound to 127.0.0.1, with 14 days of retention and a provisioned dashboard.
The privacy settings are deliberate. Prompts, responses, tool content and raw API bodies stay off. Tool names are on, because without them every custom agent shows up as "custom". That setting also logs Bash command text, so the backend stays local.
/tether:ledger fills a run report's Tokens, Tool uses and Wall time columns from that data
and adds Tool failures and Hook blocks. The column that matters most stays human: findings that
changed code. Measuring it is how I learned that per-task reviewers found about one code-changing
issue per 100k tokens, while one branch-wide review found about four.
06. Testing the harness itself
When you change an agent's prompt, swap a model or edit a skill, how do you know it didn't get worse?
/tether:eval replays frozen tasks taken from past runs. Each task records a base commit,
the original brief, the paths the work should touch, and the review findings it must still catch.
- Every task runs in a throwaway git worktree with a timeout, and the worktree is removed on success, failure, Ctrl-C or the next run.
- The project's verify command decides pass or fail; the score adds path coverage, caught findings, tokens and wall time, and a diff against the previous run.
- The answer key never reaches the agent: task files and results are excluded from the worktree. (A review caught an early version that copied them in.)
- Real runs are never pre-approved. They cost real tokens, so the user starts them.
07. tether-nx: opinions with ids
The second plugin is for Angular and Nx teams. It ships six agents (an investigator, a spec-writer, a
planner, an implementer, a mechanical implementer, and a reviewer with conventions and
pairings modes) and nine skills. At its centre is angular-nx-conventions: 25 rules,
each with a stable id.
a few of the 25 rulesNX-2 layers point down; only feature and shell libraries are scope-private
FEAT-1 a feature library is sliced by screen, never by technical folder
DI-2 no providedIn: 'root' for a domain service
SIG-1 computed() params → resource({ params, loader }) → Promise-returning services
CMP-1 standalone, OnPush, the project's prefix, new control flow
These are my defaults, the rules from the folder
structure and Nx layout articles. Every team has
exceptions, so every agent and skill opens with the same sentence: "Read
.claude/tether-nx.md first; where it differs from this file, it wins." A project
overrides a rule by its id (- DI-2: our legacy store stays in root) and states its facts
there: alias, UI library, state library, language files and verify command. The planner cites rule ids,
the implementer follows them, and the reviewer checks a diff row by row against them.
It also vendors two MIT skills, ng-performance and ng-accessibility, with
their licences and a NOTICE listing every change. It points to the Angular team's own
angular-developer skill rather than copying it.
08. Built by the workflow it ships
Both plugins were built the way they ask you to work. The sequence: a brainstorm, a written spec I approved, a plan of at most twelve tasks, implementer subagents running in parallel waves, a batched review per wave, a final whole-branch review, and gates where I confirmed every settings change.
| Release | Agent runs | What the reviews caught that tests did not |
|---|---|---|
| tether 0.1 | 30 | a guard message that would have failed its own live test; Prettier fetching unpinned code from npm; claude.ai connector names bypassing the write guard; evals leaking the answer key |
| tether-nx 0.1 | ≈30 | file paths not scanned for private terms; a duplicated rules sentence in vendored skills; examples that still described the source project by shape |
The agents and skills came from a private company repository, and the plugin is public. A rename is not enough: a library name, a ticket number or a project's habits can survive one.
A neutrality test runs on every commit. It checks every published file, every file path and every commit message against a local, git-ignored denylist of company, product and people terms. It also flags ticket-shaped numbers and unknown npm scopes. The final review then hunted for anything identifying "by shape": an example's cookie names, a capture syntax, a familiar verify command. Those were rewritten before release.
09. Try it
from your project directoryclaude plugin marketplace add AndriiSiuta/tether#v0.2.0
claude plugin install tether@tether --scope local # guards, telemetry, evals
claude plugin install tether-nx@tether --scope local # Angular/Nx agents and skills
- Guards: add a
.claude/harness.jsonwith your protected branch and local-only paths. Without it, tether stays inactive. - Settings: run
/tether:setup --dry-runto see what it would add to your deny list, MCP pins and telemetry settings. - Angular/Nx: add a
.claude/tether-nx.mdwith your alias, state library and any rule you override. Then dispatchtether-nx:planneron a spec, ortether-nx:reviewerwithMode: conventionson a branch.
10. Status and what is next
tether 0.2.0 runs daily on a production Angular/Nx monorepo. tether-nx 0.1.0 is early. Its rules and agents come from that same workspace, but the plugin agents have not been run on other repositories yet. Rule ids are stable; wording will change as other codebases push back.
Next on the list:
- Twenty eval tasks instead of five.
- A parity run of the plugin agents against the originals.
- A tracker-neutral PR agent.
- A sandbox profile tested over a full week.
Issues and pull requests are welcome. If a rule does not fit your workspace, that is exactly the feedback the ids exist for.
Agents are fast. A tether decides where they can go.