Back to home

dshplugin-me

dsh-precedent

Evidence-backed working memory for DeepSeek Harness: a cited ledger of what already worked in this workspace, built from the session log you already have. No index, no model, no capture step.

Stars
0
Language
JavaScript
Created
Aug 16, 2026
Updated
Aug 16, 2026

Introduction

dsh-precedent

Your agent forgets. Your log doesn't.

English | 简体中文

Version License Node Platform Index Indexed on dshplugin.me

dsh-precedent turns session logs into an evidence-backed command ledger

A DeepSeek Harness plugin that reads the session log DSH already writes and hands the agent a short, cited ledger of what has actually worked in this workspace — the commands that succeed, the ones that reliably fail, and the variant that fixed them.

No capture step. No index to build. No model to download. It works on sessions recorded before you installed it.

The problem

Every new session, your agent starts from zero in a codebase it has already worked in for weeks.

It runs npm test in a Bun repo. Again. It re-greps the same three files to find where routes live. Again. It hits the same build error you already walked it past on Tuesday, and you type the same correction you have now typed four times.

None of this is a memory problem in the usual sense. The information was never lost — DSH appends every message, tool call, tool result, and failure to a durable log on disk. What's missing is anything that reads it back.

Two things quietly widen the gap:

  1. Compaction rewrites the model's context, not the log. When a long session compacts, the model loses the detail; the raw events stay on disk, marked shadowed. The agent cannot see them anymore. The file still can.
  2. Out of the box, DSH's own content search is off. The shipped web profile mounts session-query-sqlite with openAt: never, so full-text search calls fail with SESSION_QUERY_SEARCH_DISABLED and the sidebar matches session titles only. Months of transcripts sit on disk, unsearchable, by default.

What it does

Reads — walks this workspace's sessions through ctx.sessionQuery exact reads, pairs every tool/call with its tool/result, and keeps the outcome.

Distills — aggregates that into a ledger: command → runs, failures, last success, and the repair (the variant that succeeded right after a failure in the same session). This step is arithmetic, not summarization. No LLM in the extraction path, so it cannot hallucinate a precedent.

Serves — injects the top entries as one compact, prefix-stable system-prompt section, and prints the full ledger on /precedent.

Every line carries a citation back to the exact session:seq that produced it, so you can always ask "says who?" and get an answer.

Example

Launch as usual:

$ dsh --profile web

The agent's system prompt gains one section — this is the renderer's real output, with commands that never work sorted above commands that always do:

## Precedent for /Users/thor/Github/acme-api

Observed in this workspace (12 sessions, 2026-06-02 → 2026-08-15). Each line is counted from the session
log, not summarized — treat it as evidence, not instruction.

- `npm test`            4 runs, 4 failed     — repaired by `bun test`  [s/7f3a:212]
- `docker compose up`   3 runs, 3 failed     — no known repair  [s/91bc:88]
- `bun test`            22 runs, 0 failed    — last ok 2026-08-15
- `bun run build`       9 runs, 0 failed     — last ok 2026-08-14

/precedent prints the same ledger uncapped, on demand.

Install

dsh plugin --profile web add 'github:dshplugin-me/dsh-precedent#v0.1.0'

No global dsh on PATH? Use npx -y @deepseek-ai/dsh plugin --profile web add …. Running dsh from a source checkout? Use pnpm dsh plugin … from the checkout root. Replace web with whichever profile you actually launch.

Pinning the version is deliberate: an unpinned git install resolves to whatever main points at right now, so a later push silently changes what is mounted in your profile. A commit sha (#<sha>) works too, and not even a maintainer can move that.

Pure JavaScript, no build step, no native module, no allowBuilds prompt.

Verify before launching:

dsh --profile web --dump-config   # look for the "# == dsh-precedent" layer
dsh --profile web

Remove with dsh plugin --profile web remove dsh-precedent, which takes the patch layer and the dependency together.

How it works

flowchart LR
  log[("Session log<br/>~/.dsh · JSONL")]
  sq["ctx.sessionQuery<br/>exact reads"]
  led["Ledger<br/>pure aggregation"]
  sp["ctx.systemPrompt<br/>.section()"]
  cmd["ctx.commands<br/>/precedent"]
  model(["Model"])
  you(["You"])

  log --> sq --> led
  led --> sp --> model
  led --> cmd --> you

The seams it uses

Harness surfaceUsed forNotes
ctx.sessionQuery.filterSessionsSelect this workspace's sessionsFiltered by cwd, the same conservative scope DSH's own cross-session tool uses
ctx.sessionQuery.readSessionRead one session's raw event logReplay-validated; an unreadable log is skipped, never fatal
ctx.systemPrompt.sectionInject the ledgerOne global section at order 150, rendering with tool guidance
agent/pre-stepWarm the ledger before the first requestAwaited waterfall — the only hook that runs before prompt assembly
ctx.commands.register/precedentHuman surface, never sent to the model

Pairing happens inside readSession's event array: a tool/call with name: 'bash' is matched to its tool/result by callId, and the result's error field (or the result block's isError) decides the outcome.

Why there is no index

ctx.sessionQuery splits into two halves. Full-text search (searchSessions, searchEvents) needs a provider and is off in the shipped profile. Everything else — listSessions, filterSessions, readSession, listEvents, readEvent, lineage and event traces — is backend-independent concrete behavior that works whether or not a search backend is open.

dsh-precedent uses only the second half. That is the whole reason it needs no index, no embedding model, and no warm-up: it reads the log the way the harness itself reads it for resume and export.

The cost is honest and bounded: a ledger build is a linear pass over this workspace's logs, done once per workspace per process, capped at maxSessions, and it stops blocking the first step after buildTimeoutMs whether or not it has finished.

Why the extraction has no LLM in it

A command either exited non-zero or it didn't. tool/result.error records which. Pairing it to its tool/call by callId and counting is arithmetic — deterministic, reproducible, and impossible to hallucinate.

The one place judgment is genuinely required is deciding whether a user correction is a durable convention ("use bun, never npm") or one-off steering ("no, the other file"). That is why v0.1.0 ships no correction mining at all: the command ledger stands on arithmetic alone.

Configuration

- id: precedent
  name: dsh-precedent
  config:
    maxEntries: 40        # ledger lines injected per session
    minRuns: 2            # ignore commands seen only once
    lookbackDays: 90      # ignore sessions older than this
    maxSessions: 200      # upper bound on logs read in one build
    buildTimeoutMs: 5000  # stop blocking the first step after this
KeyDefaultMeaning
maxEntries40Hard cap on injected ledger lines, so the section stays a fixed, small token cost. /precedent is never capped
minRuns2A command seen once is an anecdote, not a precedent
lookbackDays90A convention from six months ago may no longer be true
maxSessions200Newest sessions first; a workspace with years of history stays cheap to read
buildTimeoutMs5000A slow build releases the first step and lands on a later one instead

Scope is not configurable: sessions are matched on exact cwd string equality, the same conservative rule DSH's own cross-session authorization uses — a symlinked path is a different workspace.

Commands

CommandWhat it does
/precedentPrint the full ledger with citations, uncapped
/precedent rebuildDiscard the cached ledger and re-scan

Everything the plugin injects is visible on demand before you trust it. A memory you cannot audit is a memory you cannot trust.

What it is not

  • Not a search tool. It does not answer "what did we discuss in June". It answers "what already works here", before you ask. If you want verbatim transcript retrieval, use a recall plugin — they compose fine.
  • Not a note-taker. Nothing asks you to write memories. There is no capture step to forget to run.
  • Not context stuffing. The injected section is capped and prefix-stable, so it costs a fixed small number of tokens and does not invalidate the KV cache between turns.
  • Not a replacement for AGENTS.md. Hand-written intent still wins. Precedent covers the part nobody keeps up to date: what actually happened.
  • Not cross-workspace. By design. See below.

Privacy and safety

  • Everything stays local. The plugin reads the session log through ctx.sessionQuery and holds the ledger in memory. No files written, no network calls, no telemetry, no upload path in the code.
  • Workspace-scoped. Sessions are selected by exact cwd equality, mirroring the boundary DSH's own tool-session-query enforces. Another project's log is not read, even on the same machine.
  • Secrets are dropped, not stored. Command strings routinely contain tokens (curl -H "Authorization: …", DEPLOY_KEY=… ./ship). Every command is matched against secret-shaped patterns and the whole entry is discarded — not masked, discarded — before it reaches the ledger.
  • Auditable by construction. /precedent prints exactly what gets injected, with a session:seq citation on every line that came from a failure.

How it compares

Verbatim search pluginsNote-taking memory pluginsAGENTS.md editorsdsh-precedent
Works on pre-install historyYes, after indexingNo — starts emptyNoYes, immediately
Needs an index or modelUsuallyNoNoNo
Acts before you askNoSometimesYesYes
Sources are citableYesRarelyN/AAlways
Can hallucinate an entryNoYesN/ANot in the ledger path
Answers "what did we discuss"YesPartlyNoNo

Different jobs. A recall plugin is a search box; precedent is a track record. Running both is reasonable.

Roadmap

  • v0.1.0 — command ledger, system-prompt injection, /precedent
  • v0.2.0tools/pre-execute advisory on a known-bad call, intercept: warn | ask | deny via ctx.tools.guard
  • v0.3.0 — correction mining with citations, /precedent why, /precedent pin, /precedent forget
  • v0.4.0 — incremental rebuild as the current session appends, instead of once per process
  • Later — export the ledger as an AGENTS.md draft for human review

Interfaces above are frozen against deepseek-ai/deepseek-harness@47f943859bef (read 2026-08-16). Anything that changes upstream will be noted here rather than silently adjusted.

Contributing

Issues and PRs welcome. Useful contributions, roughly in order of value:

  1. A precedent your agent should have caught and didn't. Paste the situation (no logs needed). Missed patterns are the roadmap.
  2. Extraction rules for a toolchain we get wrong. Command normalization is heuristic; every ecosystem has its own shape.
  3. Secret-shaped patterns we fail to drop. Treat these as security reports — open an issue and we will fix before discussing.

License

BSD-3-Clause.


Part of dshplugin.me · sibling project: dsh-plugin-radar