$ claude plugin install tether@tether --scope local▊

Two memories and a guard rail

Two days putting memory and guard rails around Claude Code on a production Angular monorepo. claude-mem gave the sessions a memory nobody curates, with guardrails set before its first run. tether gave them hooks that cannot be talked out of, once they were actually installed. And a scorecard out of 50 gave "is the harness good enough" an answer that moves.

Andrii Siuta · October 2026 · ~8 min read Claude Code claude-mem tether
38 → 45harness score, of 50
2memories
39hooks in a session
4releases in two days
5 / 5eval cases passing
251tests

github.com/AndriiSiuta/tether

01. Where we started

On the morning of 10 October the harness around my Claude Code sessions had two gaps I did not know about. The project-local hooks, Prettier after every edit, the implementer report check, the git guard, lived in a settings.local.proposed.json that Claude Code never reads. No hook had run in that repository for weeks. And memory was a single layer: a curated folder of Markdown facts, good for rulings and pull-request state, blind to the tool-call history that produced them.

The goal for the day was to close both gaps with two plugins that already existed. claude-mem adds a searchable, automatically written history of what every session did. tether, my own plugin, carries project-neutral hooks that read every project value from a .claude/harness.json. The second goal was to put a number on the harness as a whole: a score out of 50, so that "is it good enough" has an answer that moves.

A point is claimed when something runs and is seen to run, never when it is written.

02. Two memories

claude-mem runs a second, smaller model beside every session. It watches the tool calls, writes short observations into a local database, and injects a compact summary of recent work at the start of the next session. The result is a memory nobody has to curate. The cost is that the observer is a model too: billed to the same plan, invisible in /cost, and it sees everything the main session sees, including file contents.

I set the guardrails before the first run, in the global settings, so they apply in every repository:

  • No observations from subagents. A planning run dispatches a dozen agents; without this the observer re-reads every one of them.
  • Skip the output of the verify script, nx and jest. Test logs are the longest and least useful thing to summarise.
  • Redaction on, so a token that passes through a tool result never reaches the database.
  • The file-read gate off, and the plugin's own telemetry off.

Of the plugin's 22 skills I keep four in use: memory search, structural code exploration, the explainer, and the agent cost report. The other 18 duplicate agents the harness already has, or send data out. The lesson of the day was how to switch them off.

challenge

The skillOverrides setting looks right and does nothing: Claude Code documents that plugin skills are not affected by it. Eighteen entries, a reload, and all 22 skills still listed.

how we solved it

The switch is a sentence in the project's local instructions that names the four allowed skills and forbids the rest, with a per-skill permission deny as the harder option if that ever fails.

The two memories now divide the work. The curated file memory keeps rulings, review bars and PR state, written on purpose. claude-mem keeps the searchable history of what was tried, written by itself. The investigator agent queries it once per ticket for earlier work on the same area, and treats what comes back as data, like a ticket.

03. Guard rails that do not trust the model

tether is the part of the harness that does not trust the model. Its hooks run as shell processes around tool calls and read one file, .claude/harness.json:

  • A git guard blocks a commit or push on a protected branch, one that carries a local-only path, and a commit whose text carries a token shape.
  • A deny list blocks MCP tools that write where they should not: a design file, a wiki page.
  • A reminder lands on every result from an untrusted source, a ticket, a web page, a memory search, and once at session start.
  • An implementer's report must carry the fields a reviewer needs. A spec has a word cap. A fix loop has a round cap.

Four releases went out across the two days, each on green CI:

VersionDateWhat changed
0.4.211 OctThe git guard judges only the executable text of a command; a repository's own harness.json governs it
0.4.110 OctThe dashboard filters by the vcs_repository_name label the metrics carry
0.4.010 OctNested harness.json lookup, secret-shape checks in both guards, a session-start reminder, a setup --only harness scaffold, eval fixes
0.3.010 Octclaude-mem alignment: the eval runner disables memory capture, setup --only claude-mem writes the guardrails

Two of those changes came from how the day went rather than from a plan. The nested lookup exists because the frontend sits two folders below the git root and the old guard looked only at the root. The secret-shape check exists because the guard already saw every commit command, so refusing one that carries a GitHub, Anthropic, AWS or Azure DevOps token shape cost a dozen lines.

04. Getting the hooks live

Enabling the plugin was not enough. After writing "tether@tether": true into the project's local settings, two reloads and a fresh config file, a probe commit against a local-only folder still went through. Claude Code loads a plugin only when the project also has an install record for it, and mine existed for two other directories. One command from the project fixed it:

claude plugin install tether@tether --scope local

Then /reload-plugins reported 10 plugins and 39 hooks where there had been 9 and 31, and the probe came back blocked: the commit names a local-only harness path. That was the last provisional point on the scorecard.

The guard tripped within the hour. A Python heredoc that edited the day's notes mentioned the probe command in a string, and the guard matched git commit anywhere in the Bash text. The next morning the fix went in as 0.4.2. The guard now strips heredoc bodies and plain quoted strings before deciding whether a command is a commit, but keeps a quoted string when it is the program of an interpreter, so bash -c "git commit …" and eval are still judged. The path check keeps quotes, so a quoted pathspec still blocks. The secret check still reads the whole text.

Shipping that fix needed a second change. The guard applied the frontend project's config to the plugin repository, whose commits land on main, so the release commit would have been blocked from the same session. A repository with its own harness.json is now governed by it, and falls back to the session project's config only when it has none.

05. The score

The harness scores 45 of 50, up from 38 on the morning of 10 October. Ten areas, five points each, scored on evidence.

Harness scorecard, ten areas, before and after Tests and CI 5 to 5. Docs 5 to 5. Guards 4 to 5. Untrusted input 4 to 5. Observability 4 to 5. Setup 4 to 5. Agents 4 to 4. Process controls 4 to 4. Evals 2 to 4. Adoption 2 to 3. Total 38 to 45 of 50. 012345 Tests and CIDocsGuardsUntrusted inputObservabilitySetupAgentsProcess controlsEvalsAdoption 4 of 54 of 54 of 53 of 5 grey: 10 Oct morning, 38 of 50 · pink: now, 45 of 50
Scored by hand on 10 and 11 October 2026. Six areas at full marks; adoption the furthest from it.

Guards and untrusted input got to five through the secret-shape checks and the session-start reminder. Observability and setup got there through the dashboard's repository filter and the scaffold, both confirmed by running them. Evals moved the most, from two to four, once all five cases passed for reasons that hold. Adoption moved from two to three when the hooks were seen to fire in a live session.

The last five points are not cheap. Evals wants a case for tether's own hooks, which needs a git repository as a fixture and Bash allowed inside the eval sandbox. Process controls wants a per-run cost cap enforced by a Stop hook that reads the telemetry ledger. Adoption carries two of the five: one for weeks of everyday use without a bypass, one for a second user. Agents stays at four until an agent's behaviour is proven by an eval rather than by reading its prompt.

06. What to keep

What the two days taught, in the order it hurt:

  1. A hook nobody has seen fire is not installed. Probe every guard with a command it must block, and read the block message, before claiming the point.
  2. Enabling a plugin and installing it are two different things. Without an install record for the project, enabledPlugins enables nothing, and a reload reports success.
  3. skillOverrides does not reach plugin skills. The switch for a plugin skill you do not want is an instruction the model reads, or a permission deny.
  4. The eval runner never says where the fixture is. Stage it into the working directory with a scaffold script instead.
  5. A guard that greps the whole command text blocks the notes about itself. Judge the executable text, keep interpreter arguments, keep the stricter checks on the whole text.
  6. A session's project config must not govern a different repository. The repository's own policy wins; the project's applies only where there is none.
  7. Set the observer's guardrails before its first run. Subagent skipping and redaction are cheap on day one and expensive to retrofit after a month of unredacted logs.

Telemetry and evals, the two ways I know any of this is true, have their own article: Telemetry and evals: how we know the harness works.

Two memories so the agent remembers. One guard rail so it does not matter when it forgets.