$ claude plugin marketplace add AndriiSiuta/tether▊

tether: AI coding agents on a short leash

I open-sourced the harness I run AI agents with on a production Angular monorepo. It is two Claude Code plugins in one marketplace. tether puts guards, approval gates, local telemetry and evals around any agent. tether-nx gives Angular and Nx teams agents and skills built on rules with stable ids. This article explains why each piece exists, what it does, and how the plugins themselves were built and reviewed by agents.

Andrii Siuta · October 2026 · ~12 min read Claude Code open source Angular · Nx
2plugins, one marketplace
7hooks
6 + 9agents + skills for Angular/Nx
25rules with stable ids
230tests
MITlicence

github.com/AndriiSiuta/tether

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:

CriteriaCount
Met16
Partly met18
Gap15

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.

tether hooks in the agent loop An agent calls a tool. Before the call, the git guard and the MCP write guard can block it. After the call, untrusted-input marks external text as data and Prettier formats edited files. When a subagent stops, the report check, the spec word cap and the fix-round cap can send it back. Telemetry flows to a local OpenTelemetry stack. agent tool call PreToolUse · can block git guard (branch, local-only paths) · MCP write guard (deny list) the tool runs PostToolUse · annotates untrusted-input · Prettier SubagentStop · can send back report check · spec cap · fix-round cap all events → OpenTelemetry → a localhost Grafana stack (tokens, cost, tool failures, blocks, per-agent traces)
Pink stages can stop the agent; green ones only add context. Every hook reads its project values from .claude/harness.json, and in a repository without that file every hook does nothing.

03. Guards that cannot be talked out of

GuardStopsWhy it exists
git guarda commit or push on a protected branch; a commit that names a local-only filea shared checkout once had the wrong branch reset, and private harness files must never reach a PR
MCP write guardwrite tools of configured servers (design tools, trackers, code-quality services), by exact name or pattern, including claude.ai connectorsa shell deny list does not cover MCP tool names; the guard is mirrored into permissions.deny as a second layer
untrusted-inputnothing; it marks every result from a ticket, PR thread, design file or web page as dataprompt injection arrives through exactly those channels
report checkan implementer that stops without Files:, Checks:, Deferred: and Plan edits:manual checks used to be skipped silently
fix-round capa task that needs more than two fix roundspast two rounds, an agent rarely converges; escalate to a human instead
spec word capa spec over the configured lengthan 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.

challenge

A plugin cannot set permissions, environment variables, the sandbox or telemetry. Those belong to the user's settings, by design.

how we solved it

/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:

  1. The design. The human approves the brainstormed approach before a spec is written.
  2. The spec. The human reads it before a plan is made.
  3. 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.
  4. Escalations. The fix-round cap hands a stuck task back with the failing check.
  5. 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.

ReleaseAgent runsWhat the reviews caught that tests did not
tether 0.130a 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≈30file paths not scanned for private terms; a duplicated rules sentence in vendored skills; examples that still described the source project by shape
challenge

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.

how we solved it

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.json with your protected branch and local-only paths. Without it, tether stays inactive.
  • Settings: run /tether:setup --dry-run to see what it would add to your deny list, MCP pins and telemetry settings.
  • Angular/Nx: add a .claude/tether-nx.md with your alias, state library and any rule you override. Then dispatch tether-nx:planner on a spec, or tether-nx:reviewer with Mode: conventions on 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.