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
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:
- 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. - Out of the box, DSH's own content search is off. The shipped
webprofile mountssession-query-sqlitewithopenAt: never, so full-text search calls fail withSESSION_QUERY_SEARCH_DISABLEDand 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 surface | Used for | Notes |
|---|---|---|
ctx.sessionQuery.filterSessions | Select this workspace's sessions | Filtered by cwd, the same conservative scope DSH's own cross-session tool uses |
ctx.sessionQuery.readSession | Read one session's raw event log | Replay-validated; an unreadable log is skipped, never fatal |
ctx.systemPrompt.section | Inject the ledger | One global section at order 150, rendering with tool guidance |
agent/pre-step | Warm the ledger before the first request | Awaited waterfall — the only hook that runs before prompt assembly |
ctx.commands.register | /precedent | Human 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
| Key | Default | Meaning |
|---|---|---|
maxEntries | 40 | Hard cap on injected ledger lines, so the section stays a fixed, small token cost. /precedent is never capped |
minRuns | 2 | A command seen once is an anecdote, not a precedent |
lookbackDays | 90 | A convention from six months ago may no longer be true |
maxSessions | 200 | Newest sessions first; a workspace with years of history stays cheap to read |
buildTimeoutMs | 5000 | A 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
| Command | What it does |
|---|---|
/precedent | Print the full ledger with citations, uncapped |
/precedent rebuild | Discard 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.sessionQueryand 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
cwdequality, mirroring the boundary DSH's owntool-session-queryenforces. 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.
/precedentprints exactly what gets injected, with asession:seqcitation on every line that came from a failure.
How it compares
| Verbatim search plugins | Note-taking memory plugins | AGENTS.md editors | dsh-precedent | |
|---|---|---|---|---|
| Works on pre-install history | Yes, after indexing | No — starts empty | No | Yes, immediately |
| Needs an index or model | Usually | No | No | No |
| Acts before you ask | No | Sometimes | Yes | Yes |
| Sources are citable | Yes | Rarely | N/A | Always |
| Can hallucinate an entry | No | Yes | N/A | Not in the ledger path |
| Answers "what did we discuss" | Yes | Partly | No | No |
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.0—tools/pre-executeadvisory on a known-bad call,intercept: warn | ask | denyviactx.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.mddraft 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:
- A precedent your agent should have caught and didn't. Paste the situation (no logs needed). Missed patterns are the roadmap.
- Extraction rules for a toolchain we get wrong. Command normalization is heuristic; every ecosystem has its own shape.
- Secret-shaped patterns we fail to drop. Treat these as security reports — open an issue and we will fix before discussing.
License
Part of dshplugin.me · sibling project: dsh-plugin-radar