DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

liyixuan201211 /

liyixuan201211/dsh-deadend

Verified

A refutation ledger for coding agents: remember what did not work, and expire it automatically when the code it depended on changes. DSH plugin + CLI, no build step.

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@7f32e3b5

dsh-deadend

Remember what did not work — and expire it automatically when the code it depended on changes.

A refutation ledger for coding agents, as a DeepSeek Harness plugin.

dsh plugin --profile web add github:liyixuan201211/dsh-deadend

No install needed to try it:

npx --yes github:liyixuan201211/dsh-deadend --help

中文:一个失败账本。agent 每一轮新会话都会用同样的乐观重试同样的失败,代价重复支付。本插件把"试过且走不通"的方案记在仓库里,并在下一次尝试前先查一遍。关键在于:每条记录都带锚点(决定其真伪的文件的哈希)——锚点没变就拦截,锚点一变就自动降级为"待复测",因为当初那个世界已经不在了。


The problem is not forgetting. It is remembering badly.

Every memory system for agents stores what worked: facts, preferences, summaries, embeddings. Almost nothing stores what failed. So the failure loop repeats — the same broken install, the same two-hour detour, once per session, forever.

The obvious fix is a list of things that did not work. The obvious fix is also wrong, and worse than nothing:

A note from six months ago saying "X does not work", written against code that has since changed, will confidently block a fix that now works.

A stale block is not a harmless annoyance. It is the tool lying to you, and a tool that lies gets switched off. The hard part of negative knowledge is not recording it — it is invalidating it.

The idea: anchors

Every entry carries anchors: the files whose content would have to change for the failure to stop being true.

Anchors Meaning check exit
all unchanged authoritative — it still fails 3
any changed / deleted suspect — re-test it 4
nothing known clear 0
entry refused (no anchors) record declined 5

Validity is judged by content, not by a clock. No MAX_AGE_DAYS, no calibration — and it is self-maintaining, because nobody has to remember to expire anything.

record refuses to create an entry with no anchors. That is enforced, not advised:

$ deadend record --title "go build fails on cgo" --cmd "go build ./..."
Refusing to record: no anchors.

A dead end with nothing to watch can never expire, and a claim that can
never expire is indistinguishable from a bug. Name the files whose content
would have to change for this failure to stop being true.

Suggested anchors (they exist here):
  --anchor package.json

Or pass --unanchored if this genuinely has no local artifact — it will
block forever, and `deadend status` will keep saying so.

The undecayable case exists (an upstream API limit, a vendored dependency) and --unanchored allows it — loudly, and counted in deadend status, so you always know which of your claims are unfalsifiable.

What it looks like

Before re-running something expensive, ask:

deadend check --cmd "npm install sharp" --log /tmp/last-install.log
⛔ BLOCKED — 1 recorded dead end matches, and its anchors are unchanged.

────────────────────────────────────────────────────────────────────────
dd_50e127c7d9c5  npm install sharp fails: no prebuilt binary for darwin-arm64
    matched   identical command
    recorded  2026-09-13 (today)
    attempt   npm install sharp  →  exit 1
    symptom   Error: Cannot find module sharp-darwin-arm64.node
    why       sharp ships prebuilt binaries and none exists for this platform/arch
    instead   npm rebuild sharp --build-from-source
    anchors   2 of 2 unchanged — still authoritative
      ✓ package.json
      ✓ package-lock.json
    evidence  install.log
────────────────────────────────────────────────────────────────────────

This is not a prohibition — it is a claim that was true, with its evidence.
If you think the world moved, re-test it and re-confirm:
  deadend verify dd_50e127c7d9c5 --still-fails --log <new-output>

Now change one line of package-lock.json and ask again:

⚠ SUSPECT — 1 recorded dead end matches, but the world it described has changed.

    anchors   1 of 2 changed — no longer authoritative
      ✓ package.json
      ✗ package-lock.json  f25e7d29 → 55eaf97b

Re-test it: it may now work.
  still fails?  deadend verify dd_50e127c7d9c5 --still-fails
  works now?    deadend verify dd_50e127c7d9c5 --now-works

That transition — authoritatively blocked, then honestly downgraded the moment its evidence moved — is the whole plugin.

Recording a failure

deadend record \
  --title "npm install sharp fails: no prebuilt binary for darwin-arm64" \
  --cmd "npm install sharp" --exit 1 --log /tmp/last-install.log \
  --why "sharp ships prebuilt binaries and publishes none for this platform/arch" \
  --retry "npm rebuild sharp --build-from-source (needs libvips)" \
  --anchor package.json --anchor package-lock.json \
  --evidence /tmp/last-install.log --tag native

--log is worth the trouble. The failure is hashed in a normalised form — line and column numbers, absolute paths, temp directories, digests, UUIDs, timestamps, durations and ANSI colour removed — so the same breakage is recognised across sessions, machines, and differently-worded commands:

# Recorded as:  python app.py        + ModuleNotFoundError: No module named numpy
# Still caught: python3 app.py --verbose, same failure, different command
deadend check --cmd "python3 app.py --verbose" --log ./run.log   # -> exit 3

Commands

deadend check   [--cmd C] [--log F|-] [--symptom T] [--title T] [-q] [--json]
deadend record  -t TITLE [--cmd C] [--exit N] [--log F|-] [--why W] [--retry R]
                [--anchor PATH]... [--unanchored] [--evidence E]... [--tag T]...
deadend verify  <id> (--still-fails | --now-works) [--log F] [--note N]
deadend list    [--status active|suspect|retired] [--all] [--json]
deadend show    <id> [--json]
deadend status  [--json]        counts, plus the entries that need attention
deadend merge   <ledger.jsonl...>                (use - for stdin)
deadend gc      [--dry-run] [--drop-retired] [--drop-undecayable]
deadend init

Exit codes are the interface, so check composes with any shell or CI gate:

deadend check --cmd "npm install sharp" -q || echo "already ruled out; not retrying"

Matching is layered, so it never cries wolf

Strength Match Decisive?
3 identical failure signature yes
2 identical normalised command yes
1 same command family, or similar title no — shown as related

Strength-1 matches never change the verdict. npm install sharp failing says nothing about npm install left-pad, so installing something else is never blocked — it is mentioned, with the earlier note attached. A gate that blocks too much gets disabled, which protects nobody.

Command normalisation strips whitespace and leading wrappers (sudo, time, env) and nothing else. Flags are part of the command.

The ledger lives in your repository

<repo>/.deadend/ledger.jsonl     # append-only event log, one JSON object per line

Not in ~/.cache, not per-machine. That is the design decision with the most consequences:

  • Reviewable. It arrives in a pull request as + {"v":1,"event":"record",…} — a claim you can argue with.
  • Shared. Your next session, your teammates, their agents, and CI all inherit it. A per-machine cache protects exactly one machine.
  • Auditable. Every change keeps its reason: verify appends an observation rather than overwriting, and gc folds history into the entry so compaction never loses one.
  • Concurrency-safe. Recording appends; there is no read-modify-write to race.

Identity is content-derived (dd_ + sha256(title | command | signature | anchor paths)), so two clones that record the same refutation produce the same id and merging ledgers is a set union, not a de-duplication problem.

Full format: skills/deadend/reference/schema.md.

Merging ledgers

Identity is content-derived, so the same refutation recorded twice — by two teammates, or by you on two machines — has the same id, and merging is a set union rather than a de-duplication problem. That is what makes committing the ledger to a shared repository workable in the first place.

deadend merge ../other-clone/.deadend/ledger.jsonl   # or - for stdin
#   added     3
#   updated    1
#   unchanged  7
#   total     11

When both sides know an id, the more recently updated observation wins on status and anchors, and histories and notes are unioned so neither side loses one.

Use it in CI

check reports through its exit code, so the ledger becomes a gate in one line:

- name: Do not re-run a known dead end
  run: |
    npx --yes github:liyixuan201211/dsh-deadend check --cmd "npm install sharp" -q
    # exit 3 = still authoritative: fail
    # exit 4 = anchors changed: re-test, do not fail
    # exit 0 = clear

And because an unfalsifiable entry is the failure mode this tool exists to prevent, it is worth failing the build when the ledger itself drifts:

- name: Keep the ledger falsifiable
  run: |
    npx --yes github:liyixuan201211/dsh-deadend status --json > ledger.json
    node -e '
      const j = require("./ledger.json");
      if (j.attention.length > 0) {
        console.error("ledger needs attention:");
        for (const a of j.attention) console.error("  " + a.id + "  " + a.why);
        process.exit(1);
      }'

Honest positioning

Recording failures is not a new idea, and this plugin does not claim it is. The closest prior art is a Claude Code plugin, dead-end-registry, which mines transcripts for reverted approaches; there is also academic work on failure-aware shared memory (Negative Knowledge, ICML 2026 AI4Research workshop) and "dead end registries" as a coordination mechanism in automated-research templates.

Prior art dsh-deadend
Captured reverted approaches mined from transcripts refutations you state, with evidence
Lives in ~/.claude/…, per machine .deadend/ledger.jsonl, in the repo
Match keyword match on the prompt normalised failure signature + command identity
Extraction heuristics (+ optional model pass) deterministic, offline, no model
Expiry wall-clock age (e.g. 60 days) content hash of the falsifiers
Undecayable claims not distinguished refused by default; loud when allowed
Interface editor hooks exit codes, composable with any shell or CI

Time-based expiry is wrong in both directions: too slow (a dependency bump that invalidates a dead end this morning does not clear it for 60 days) and too fast (a dead end about a frozen dependency vanishes on day 61 for no reason). The last three rows are the contribution. Reasoning in full: reference/decay.md.

Design notes

No boot-time code. cordis.patch.yml is an empty patch, deliberately. A plugin whose subject is "do not repeat mistakes" has no business inserting code into the DSH process for every profile. The payload is a skill plus a CLI that the skill runs through the visible shell tool.

An event log, not a mutable document. Append-only means no read-modify-write races, clean diffs, and a preserved reason for every status change.

suspect is never stored. It is recomputed from anchors on every read. A stored staleness flag would itself go stale — the exact bug being avoided.

Refusal over a silent bad entry. record rejects anchors that do not exist, duplicate entries, and entries without anchors, rather than writing something that will quietly mislead later.

Installing as a DSH plugin

dsh plugin --profile web add github:liyixuan201211/dsh-deadend

This installs the skill (skills/deadend/), which teaches the agent to check before it retries and record after it fails. The bundle patch adds nothing to the boot graph; review cordis.patch.yml, package.json (no lifecycle scripts) and src/ if you want to verify that.

Development

Requires Node >= 20. The sources are plain ESM JavaScript with JSDoc types, so there is no build step and no install-time script — and the published bin actually runs when installed. (They cannot be TypeScript: Node refuses to strip types for files inside node_modules, which is precisely where the package lands when it is installed or run through npx. The installable CI job guards that.)

npm test            # 74 tests
npm run typecheck   # tsc --noEmit over the JSDoc types
npm run check       # both
src/
  cli.js          exit-code contract and argument parsing
  engine.js       record / check / verify / gc, and the verdict rules
  anchors.js      hashing paths, detecting decay, suggesting anchors
  fingerprint.js  normalising failure output into a stable signature
  model.js        data model and event-log replay
  ledger.js       locating and reading/writing .deadend/ledger.jsonl
  report.js       human-readable rendering
  index.js        programmatic API

License

MIT.

—/ 5

No ratings yet

Verified DSH bundle

Commit 7f32e3b5e6e6

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout