deadrefs: find dead paths in your notes, fix only the certain ones¶
The problem¶
Notes, READMEs, runbooks and AI-agent instruction files are full of written-out paths. Reorganise your folders and those notes send every reader, human or agent, to a dead end. An agent told to read a file that is not there will often guess, and a wrong guess is worse than an error.
The method¶
Check cheaply, fix rarely, never guess.
- Listing is just existence. Pull every path out of each line, test whether it exists. No search, so it can run at every session start.
- A line about the past is not an instruction. "was in", "moved from", "old": the line is history and is skipped.
- Paths with spaces are read whole. Unquoted, the path takes the longest run of following words that exists on disk.
- Rewrite only on a unique match.
--fixindexes every name under the roots once, then tries the dead path's tail, longest first, never shorter than two parts. One hit is rewritten; two hits is a guess and is left. - One cautious fallback. No tail match? Look under the nearest folder still there, at most one level deeper than the thing used to be.
- Some places never count.
.git,node_modules,*archive*,vendor: an archived copy is the classic false match.
A one-name path (~/notes) is never rewritten. Tried on a copy of a real
notes folder, the first, looser rules made 15 rewrites, several of them into
archives or into unrelated folders that happened to share a name. With the
rules above: 5, each to the live new home.
Sample¶
The core of --fix, in Python:
def new_home(written, by): # by: every name under the roots -> paths
parts = relative_parts(written)
if len(parts) < 2:
return None # one name alone: a guess
for k in range(len(parts), 1, -1):
tail = "/" + "/".join(parts[-k:])
hits = [p for p in by.get(parts[-1], []) if p.endswith(tail)]
if len(hits) == 1:
return hits[0]
if hits:
return None # two places share the tail
return nearby_unique(written, by) # nearest live folder, one level deeper at most
Use it:
deadrefs notes/ CLAUDE.md # file:line path
deadrefs --fix notes/ CLAUDE.md # rewrite the certain ones, list the rest
Exit 0 means nothing dead is left, 1 means some is, so it fits a session-start hook or CI.
Limits¶
--fixwalks every root once: a minute or two on a large home folder. NarrowDEADREFS_ROOTS.- A unique match can still be wrong if a forgotten copy sits outside the
skipped folders. Read the
fixedlines, and keep notes in git. - It knows paths, not meaning: a path that exists but now holds something else is not flagged.
Code¶
methods/deadrefs/ in the methods repository: the script, a README and a test.