One-time audits rot. We turned ours into a standing watch.

Both of our marketplace audits, 2,019 skills, then 66,541, ended with the same caveat: this is a snapshot, and a skill can be updated tomorrow morning. This week we retired the caveat. truecopy now re-audits the entire official Claude Code plugin directory every Monday: all 255 catalog plugins, including the ~200 external vendor plugins it re-fetches at their catalog-pinned commits, and publishes the verdict to a public report and a README badge. This week’s snapshot: 1,818 skills scanned, zero poisoned. And on its very first full run, the watch caught two bugs, in itself. That story is the useful part.

Update · August 2, 2026: the watch no longer waits for Monday, it runs daily. The directory has grown with it: the run published for this date reached 274 of 276 catalog plugins (two could not be fetched), scanning 1,886 skills, zero poisoned. The cadence and counts below describe the watch as it was built in July; the live observatory always shows the current verdict.

Update · July 16, 2026: canon has a new name: truecopy. Same tool, same code; the text below uses the new name. truecopy ships on npm as @askalf/truecopy and as a GitHub Action.

Audits describe Tuesday. Supply chains don’t stop on Tuesday.

A marketplace audit is a fact about a day. The corpus it described starts drifting the moment you publish: plugins update, vendors cut releases, the catalog adds entries. If the audit was worth doing once, the delta is worth checking forever, and a check that runs forever has to be a machine, not a heroic weekend. So the audit harness became a scheduled workflow: fetch, scan, publish, every Monday, with the badge red the moment anything genuinely poisonous lands.

The target grew, too. The first version of the watch covered the plugins that live inside the claude-code repository: ten skills. The real surface is the official plugin directory: 255 catalog plugins, about 200 of them external, vendor plugins from roughly 190 repositories owned by the companies that ship them. That’s the thing users actually install from, so that’s the thing the watch now covers end to end.

The catalog is a lockfile, which is exactly the shape we know how to verify

Here’s the detail that made this satisfying to build: the official directory pins every external vendor plugin by commit hash. The catalog isn’t a list of links; it’s a lockfile. That is the supply chain done right, and it’s the same shape truecopy enforces for individual users, so the watch verifies both halves of the promise:

  • The content. Every plugin is re-fetched at its pinned commit and every skill in it is poison-scanned, the same deterministic, offline scanner from both audits, severity-tiered so documentation that mentions credentials isn’t confused with instructions that steal them.
  • The pins themselves. If a pinned commit stops resolving (rewritten history, a force-push, a vanished repo), the watch reports it as pin drift, its own section in the report. A pin that no longer resolves is supply-chain signal even when no skill is poisoned, and a watch that silently fell back to “whatever the branch says now” would be lying about what it scanned.

Fetch failures get the same treatment: counted, named, never silently dropped. If the watch couldn’t materialize a plugin this week, the report says so, because “we scanned everything” and “we scanned everything we could reach” are different claims.

What a standing watch needs that a one-time audit doesn’t

Our audits found the same thing twice: zero poison, plus a small set of flags that a human review resolves as benign (security tooling that ships the attack strings it detects, docs that quote “ignore previous instructions” in order to teach resisting it, AWS debugging guides that pipe base64 through bash as a transport). A one-time audit triages those once and moves on. A weekly watch would flag the same thirteen skills every Monday, and a tripwire that cries wolf on schedule trains everyone to ignore it, which is worse than no tripwire.

The fix came from truecopy’s own vocabulary. When you pin a skill whose findings you’ve reviewed, truecopy add --force records the acceptance for exactly those bytes, content-hash-keyed, so any change re-flags. The watch now does the same: a public accepted-findings file lists each reviewed-benign skill with its hash, its false-positive class, and the reason. Thirteen entries today, every one named in the open report with its explanation. If a vendor ships even one changed byte in an accepted skill, the acceptance dies with the old hash and the flag comes back. No pattern suppressions, no allowlist-by-name, no quiet exceptions.

The watch’s first catch was the watch

The first full-directory run published a red report: 13 poisoned, 0 accepted. The thirteen were our reviewed-benign set, the acceptances simply hadn’t applied. And the run that published this alarming result finished green. Two bugs, both real, both ours:

  • The hashes were computed over the wrong bytes. The accepted-findings hashes were generated on a Windows machine, where Git’s installer-default autocrlf quietly rewrites line endings on checkout. Every hash described CRLF bytes; the Linux runner checked out LF; nothing matched. The corpus fetcher now pins autocrlf=false: a supply-chain scanner must hash the bytes the repository actually contains, not the bytes your platform prefers.
  • A poisoned result could never have turned the run red. The workflow piped the scanner’s output through tee for logging, and in a shell pipeline, the exit code that survives is the last command’s. tee always succeeds. The scanner’s alarm exit code had been silently swallowed since version one; it was invisible for weeks because the tree was clean the whole time. One set -o pipefail fixes it.

Both fixes shipped and the re-run published what the corpus actually warranted: 255 plugins · 1,818 skills · 0 poisoned, thirteen accepted findings listed with their reasons. We’re telling this story on purpose. A tripwire you have never seen fire is decoration; you learn whether the wire is connected to anything by watching it trip. Ours tripped on day one, on its own harness, and the fact that the failure was loud and public (a red badge on the repo README) is precisely the property we wanted. The monitoring caught the monitoring. That’s the system working.

Scope and limits

The usual disclosures, because a lab that hides them isn’t one. The 609 advisories in the badge are capability mentions in skill prose, documentation touching credentials and sensitive paths, shown, never blocking; they are not findings. The thirteen accepted findings were reviewed by a human against the source; acceptance is per-byte, not per-name. The watch is a Monday snapshot of the directory’s pinned state, scanned with deterministic rules whose blind spots we’ve mapped in public; it is strong evidence, not a proof of absence. And the scan covers skill prose, the surface where poisoning lives; it is not a code audit of every vendor’s repository.

Vet, pin, verify, continuously

This is what “own your agent skills” cashes out to in practice. An audit answers was it clean when we looked; a lock answers is what I’m running what I vetted; a standing watch answers is that still true this week, and says so in public, where the answer can embarrass us if we’re wrong. The badge is on the truecopy README, the full report and per-run history live on the watch branch, and every line of the harness is in the open repo.

And if you ship or consume agent skills yourself, the same check is one line of CI: the truecopy GitHub Action fails your build the moment a pinned skill drifts or a new one scans poisonous. The official directory gets a weekly watch; your repo can have one on every push.

We build the boundaries that make agents safe to hand real capability: skills, tools, secrets, browsers. If your team ships or installs agent plugins and nobody is watching the supply chain, that’s exactly the kind of problem we go deep on.

Start a conversation →
← All writing