New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

lockwire

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lockwire

Bind documentation claims to code fingerprints. Catch stale CLAUDE.md, AGENTS.md and docs at write time inside Claude Code and Codex, with a tamper-evident history.

latest
Source
npmnpm
Version
0.1.31
Version published
Weekly downloads
436
1182.35%
Maintainers
1
Weekly downloads
 
Created
Source

lockwire

lockwire

Catch stale CLAUDE.md, AGENTS.md, and docs before your coding agent acts on them.

The mark is literal, not a metaphor: lockwiring is the aviation-maintenance technique of threading wire through two fasteners so neither can work loose under vibration — the same idea applied to a doc claim and the code it depends on.

npm Socket bundle size license: MIT Claude Code plugin Codex compatible MCP server CI

A sentence in your docs is a claim about a specific state of your code. Nothing binds the two together, so the claim rots and nobody notices — until an autonomous agent reads the stale sentence and acts on it. lockwire fingerprints the code a claim depends on, checks that fingerprint at write time inside Claude Code and Codex, and keeps a tamper-evident history of every time the claim broke.

$ lockwire check
CLAUDE.md
  DRIFTED   src/auth/session.ts#createSession (sig)

2 anchors · 1 ok · 1 drifted · 0 orphaned
single-hash would flag 1 · lockwire flagged 1 · noise −0%
$ lockwire history k7q2m9xv
2026-09-25T10:00:11.579Z  anchor.created
2026-09-25T14:12:03.656Z  anchor.drifted       sig
2026-09-25T14:15:40.726Z  anchor.resolved       re-stamped via link

Each event records which tier moved, the commit it happened on, and who ran the check that noticed (the agent and session for a hook, the person or CI job for the CLI) — see docs/ledger.md.

The problem, specifically

  • Coding agents change code faster than humans reconcile prose. Drift that used to accumulate over quarters now accumulates over an afternoon.
  • CLAUDE.md and AGENTS.md are read by an agent on every request. A stale line doesn't just mislead a person reading it later — it silently redirects an autonomous process right now. Stale agent context is a correctness problem, not a hygiene problem.
  • Wrong documentation is worse than no documentation. Macke & Doyle (NAACL 2024) found that incorrect documentation "can greatly hinder" an LLM's ability to understand code, while missing documentation barely affects it at all — see docs/research.md. A confidently wrong CLAUDE.md is actively harmful in a way an empty one isn't.

And the tools built to fix this have a shared failure mode: one hash per symbol. Hash a whole function, and a one-line body tweak invalidates a doc that only ever claimed something about the signature. Teams get flooded with false staleness, the CI gate gets marked continue-on-error, and the tool gets uninstalled. This is the specific failure lockwire is built to not have.

How it's different

lockwire fingerprints four aspects of a symbol separately, and a claim only binds the aspects it actually depends on:

flowchart LR
    SYM["Symbol<br/><b>createSession()</b>"] --> P & S & B & D

    P["<b>path</b><br/>file location"]
    S["<b>sig</b><br/>name · params · types<br/>return · modifiers"]
    B["<b>body</b><br/>normalized AST<br/>of the implementation"]
    D["<b>deps</b><br/>sorted set of<br/>outbound calls"]

    P --> C1["<i>structural claim</i><br/>'sessions are created in<br/>src/auth/session.ts'"]
    S --> C2["<i>contractual claim</i><br/>'takes a UserId,<br/>returns a Session'"]
    B --> C3["<i>behavioral claim</i><br/>'retries 3x with<br/>exponential backoff'"]
    D --> C4["<i>architectural claim</i><br/>'delegates token minting<br/>to the KMS client'"]

A contractual claim binds only sig — a body refactor never touches it. This is the whole mechanism.

A doc claiming "takes a UserId, returns a Session" binds sig only, and stays silent through every internal refactor of createSession. The P0 test suite proves this on a real example: a body-only edit that adds a logging call produces zero drifted anchors and a noise line reading single-hash would flag 1 · lockwire flagged 0 · noise −100%.

System view

flowchart TB
    subgraph SRC["Inputs"]
        direction LR
        CODE["Source tree<br/>TS · TSX · JS · Python"]
        DOCS["CLAUDE.md · AGENTS.md<br/>docs/**/*.md"]
    end

    subgraph L1["Fingerprint"]
        direction LR
        TS["tree-sitter<br/>parse"] --> EX["Tier extractor<br/>path · sig · body · deps"] --> HASH["BLAKE3<br/>fingerprint"]
    end

    subgraph L2["State"]
        direction LR
        LF[("lockwire.lock<br/>anchors")]
        LG[("ledger.jsonl<br/>history")]
    end

    subgraph L3["Three gates"]
        direction LR
        G1["write-time<br/>PreToolUse hook"]
        G2["commit-time<br/>pre-commit"]
        G3["merge-time<br/>GitHub Action"]
    end

    CODE --> L1
    DOCS -->|"&lt;!-- lockwire ... --&gt;<br/>markers"| L2
    L1 --> L2
    L2 --> L3

    classDef store fill:#1f2937,stroke:#60a5fa,color:#e5e7eb
    class LF,LG store

Anchor lifecycle

stateDiagram-v2
    [*] --> Fresh: lockwire link<br/><i>anchor.created</i>

    Fresh --> Drifted: a bound tier changed, or the claim<br/>sentence was edited<br/><i>anchor.drifted</i>
    Fresh --> Fresh: symbol renamed in the same file,<br/>exactly one match<br/><i>anchor.relocated</i>
    Fresh --> Orphaned: symbol, file or doc marker gone<br/><i>anchor.orphaned</i>

    Drifted --> Fresh: doc updated + link --reviewed,<br/>or ack<br/><i>anchor.resolved / anchor.acknowledged</i>
    Drifted --> Waived: time-boxed waiver<br/><i>waiver.granted</i>
    Drifted --> Superseded: claim no longer applies<br/><i>anchor.acknowledged</i>

    Waived --> Fresh: waiver expires, nothing moved
    Waived --> Drifted: waiver expires, something moved<br/><i>waiver.expired</i>

    Orphaned --> Fresh: marker or symbol restored,<br/>then link (--reviewed if the code changed)

    Superseded --> [*]

    note right of Fresh
        Relocation is an event, not a state: after a
        rename the anchor is just fresh (or drifted,
        if a bound tier also moved) under its new
        name. Same-file renames only in P0; a symbol
        moved to another file reports orphaned — see
        docs/concepts.md limitations.
    end note

Write-time enforcement

sequenceDiagram
    participant A as Coding agent
    participant H as PreToolUse hook
    participant L as lockwire core
    participant D as Ledger

    A->>H: Edit src/auth/session.ts
    H->>L: which anchors cover this file?
    L-->>H: 1 claim, bound to sig

    alt advisory (default)
        H-->>A: inject claim as context —<br/>no block, no prompt
    else ask
        H-->>A: prompt: acknowledge or proceed anyway
    else deny
        H-->>A: block until acknowledged
    end

    A->>A: edits the file
    A->>L: PostToolUse — re-fingerprint
    L->>D: anchor.drifted (if the bound tier moved)
    D-->>A: additionalContext: which claim just broke

Install

Claude Code — hook, MCP server, and skill in one plugin:

/plugin marketplace add Reality-Ventures/lockwire
/plugin install lockwire@lockwire

Codex, Cursor, Gemini CLI, and other agents that read SKILL.md — the skill only; wire up the hook and MCP server per your agent (see docs/agents.md):

npx skills add Reality-Ventures/lockwire

CLI and CI — no agent required:

npm install -g lockwire
lockwire check

Or without installing anything, via npx:

npx lockwire check

GitHub Action — merge-time gate (and lockwire/autofix to fix what it finds):

- uses: actions/checkout@v4
  with:
    fetch-depth: 0   # --changed needs history to find the merge base
- uses: Reality-Ventures/lockwire@v0
  with:
    args: --changed

pre-commit — commit-time gate, as a plain git hook (.git/hooks/pre-commit, made executable):

#!/bin/sh
npx --no-install lockwire check --staged --no-write

Quickstart

$ lockwire init
lockwire initialized: lockwire.lock, .lockwire/config.json, .gitattributes (ledger merge=union)

Add a marker above a claim in CLAUDE.md:

<!-- lockwire src/auth/session.ts#createSession sig -->
`createSession` takes a `UserId` and returns a `Session` valid for 24 hours.
$ lockwire link CLAUDE.md
CLAUDE.md: 1 created, 0 refreshed

The marker is now stamped with an id:

<!-- lockwire src/auth/session.ts#createSession sig id=k7q2m9xv -->

Refactor the function's body without touching its signature, then check:

$ lockwire check
CLAUDE.md
  ok

1 anchor · 1 ok · 0 drifted · 0 orphaned
single-hash would flag 1 · lockwire flagged 0 · noise −100%

Now change the actual signature (add a parameter) and check again:

$ lockwire check
CLAUDE.md
  DRIFTED   src/auth/session.ts#createSession (sig)

1 anchor · 0 ok · 1 drifted · 0 orphaned
single-hash would flag 1 · lockwire flagged 1 · noise −0%

Update the doc, then re-stamp — --reviewed is required because the code changed since the claim was stamped:

$ lockwire link CLAUDE.md --reviewed
CLAUDE.md: 0 created, 1 refreshed

And ask what happened, whenever, from anyone:

$ lockwire history k7q2m9xv
2026-09-25T10:00:11.579Z  anchor.created
2026-09-25T14:12:03.656Z  anchor.drifted       sig
2026-09-25T14:15:40.726Z  anchor.resolved       re-stamped via link

Inside Claude Code, the hook does this automatically — before the edit, you'd see the claim injected as context; after, you'd see exactly which claim just drifted, with no check invocation needed. See docs/agents.md for the injected text.

Let your agent fix it

lockwire doesn't write docs and has no model of its own. It gives the agent you already run — Claude Code, Codex, anything that speaks MCP — a work queue: every claim that's wrong, the code or value it was checked against and what it is now, and a resolve step that re-checks the fix and refuses one that's still wrong (a number that still disagrees with its data, a marker pointing at nothing). Ask your agent to run the fix-docs prompt, or do it by hand:

lockwire tasks                 # what needs attention
lockwire task k7q2m9xv         # one task: the claim, the code as stamped and now
lockwire resolve k7q2m9xv --note "documented the new scope parameter"

Every resolution is recorded with who made it and why. See docs/agents.md.

To run it unattended, lockwire autofix starts Claude Code or Codex on your own key — only when a claim is actually wrong — then re-checks every claim and refuses a run that touched anything but docs. In CI, the Action does the same and opens a pull request:

- uses: Reality-Ventures/lockwire/autofix@v0
  with:
    agent: claude-code
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}

See docs/autofix.md.

What lockwire does not do

Hashes cannot catch semantic drift — a doc saying "we use Redux" when the code moved to Zustand, where every file path still resolves and no signature changed. Catching that requires reasoning about meaning, not fingerprints; ClaudeDrift does this well as an on-demand, LLM-reasoning layer, and pairs naturally with lockwire's continuous, deterministic one — see docs/comparison.md. Numbers are the exception: a sentence bound to the value it came from — a JSON key or a LaTeX macro a pipeline writes — is checked against that value at the precision it states, so "the lower bound is 0.18" is flagged when a rerun makes it 0.19, and not when it makes it 0.1791 (docs/concepts.md). P0 supports TypeScript, TSX, JavaScript, and Python source, and values in JSON and LaTeX files; cross-file rename detection, a resolver agent, and semantic-claim extraction are roadmap, not shipped — see docs/concepts.md.

FAQ

How is this different from fiberplane/drift?

fiberplane/drift computes one AST hash per anchor — a body refactor invalidates a doc that only claimed something about the signature. lockwire fingerprints path, signature, body, and dependencies separately, so a claim only re-flags when the aspect it actually depends on changes. Neither fiberplane/drift nor any other tool we found keeps a history of why a claim broke, who broke it, or how often — lockwire's ledger does. See the full comparison in docs/comparison.md.

Does it call an LLM?

No. Detection is deterministic: tree-sitter parsing, BLAKE3 hashing, set comparison. No network calls, no API keys, no per-check cost. The write-time hook and CLI both run entirely offline.

Will it slow down Claude Code?

The hook has a 10-second timeout and only re-parses the one file that was just touched — in practice, well under a second for typical files. It fails open: if anything throws, the hook exits silently and the tool call proceeds untouched. See docs/agents.md.

What happens on merge conflicts in the ledger?

.lockwire/ledger.jsonl is append-only JSONL with merge=union in .gitattributes (written by lockwire init), so git merge unions the lines from both branches instead of conflicting. Event hashes don't chain to a previous event, deliberately — the ledger's tamper-evidence is order-independent (a BLAKE3 root over the set of event hashes), because ordering is already supplied by each event's timestamp and commit SHA. See docs/ledger.md.

Can I use it without Claude Code?

Yes. The core is a standalone CLI (lockwire check, lockwire link, …) that runs anywhere Node runs — in a pre-commit hook, in any CI system, or by hand. Claude Code and Codex hooks are two of several ways to trigger it; the GitHub Action is a third.

What languages are supported?

TypeScript, TSX, JavaScript, and Python in P0 (v0.1.0). More tree-sitter grammars are a small addition on top of the existing tier-extraction pipeline — see docs/concepts.md.

Why not just use an LLM to check everything?

Because then every check costs money and time, becomes non-deterministic, and can't run inside a 10-second write-time hook. lockwire's deterministic core handles the mechanical 80% (does this claim's bound aspect still match); an LLM-reasoning layer for the harder semantic 20% is a natural P2 addition, not a P0 requirement — see docs/research.md for why teams abandon LLM-only or hash-flood-prone tools.

Comparison

lockwirefiberplane/driftClaudeDriftpallaprolus/drift
Fingerprint granularity4 tiers per symbol1 hash per anchornone (LLM judgment)1 score per doc-code pair
Write-time agent hook✅ Claude Code + Codex❌❌ (on-demand)❌
History / ledger✅ append-only, tamper-evident❌❌❌
Semantic drift (meaning changed, refs still resolve)❌ (roadmap)❌✅✅ (opt-in, on-demand)
Runs with no LLM✅✅❌✅ (core checks)
Languages (P0)TS, TSX, JS, PythonTS, Python, Rust, Go, Zig, Javaany (LLM-read)TS/JS, Python, Go, Rust, Java

Full detail, including two smaller entrants and where each tool actually stops, in docs/comparison.md.

Documentation

Contributing

See CONTRIBUTING.md. Security issues: see SECURITY.md, not a public issue.

License

MIT

Keywords

documentation

FAQs

Package last updated on 22 Oct 2026

Related posts