jjmlovesgit/dsh-plugin-coding-delegate ↗★ 0

dsh-plugin-coding-delegate

分离规划与执行权限,禁止云端规划器直接修改代码。 适合对代码写入安全有严格要求、需要沙箱化本地执行的用户。

包名
dsh-plugin-coding-delegate
兼容性
待验证
版本
0.2.0
许可证
MIT
最近更新
2026年10月9日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:jjmlovesgit/dsh-plugin-coding-delegate

DSH Coding Delegation Technical Control for the Separation of Duties

A DeepSeek Harness (DSH) plugin that separates who specifies work from who implements it, and refuses to let the first party write the second party's code. It is a technical control, not a policy: enforcement happens in a tool-call hook before a write lands, so it does not depend on the model choosing to comply.

The metered model has no write tool and no shell. A fully prompt-injected architect can plan anything and still cannot change a byte of your host, because the permission is never granted.

How it works: a contract out, a verdict back

Coding needs two different jobs done, and they want two different contexts. The plugin gives each one its own window and lets exactly two things cross between them.

The architect is the metered thinking model. It holds the design conversation, decides how the work decomposes, and writes a contract for each unit: what to build, where the worker may write, and the command that decides whether the unit passed. It does not write source, and it does not read source back.

The worker is whatever your GPU is already running. It receives one unit and everything that unit needs — nothing else — writes the code, and is discarded. A fresh context per unit costs nothing.

DirectionWhat crossesWhat never crosses
architect → workerthe contract: instruction, permitted paths, the verification commandthe design conversation, the other units
worker → architectthe verdict: pass/fail counts, a redacted failure structure, the names of files writtenthe code

The verification command runs as an ordinary subprocess, with the operator's approval. Its raw output stays on the local machine; what travels back is the failure structure, with source stripped out.

Status: 0.2.0, The mechanisms work and are tested. Its limits are documented rather than glossed, including two that cannot be closed from inside the process — see What this control does not cover. Read that section before deciding whether it fits your situation.

What it does

Five things, in the order they act:

  1. In-process, no sidecar. No daemon, no extra port, nothing to supervise. The gate and the authorship guard run inside the plugin.
  2. A DLP gate on every outbound request. Prompts are scanned for credentials and high-entropy tokens before the cloud sees them. A hit is refused or rerouted to the local worker, never transmitted. The gate accumulates the user messages it has seen, so a credential from an earlier turn keeps it closed instead of scrolling out of view.
  3. A code guard, in both directions. Cloud-authored writes and deletions of source are denied or sent for approval — and so is reading back a file the worker wrote, since that pulls delegated code into the context delegation exists to keep it out of. Shell forms are covered, including inline program text (python -c, node -e).
  4. delegate_worker. A tool the architect calls to hand one unit of implementation to a local OpenAI-compatible server (LM Studio, Ollama, vLLM, llama.cpp, a remote gateway). File writes are contained to the workspace, verification output is redacted to structure before it travels, and the verification command itself requires approval.
  5. A unit is judged, not reported. The contract is pinned with contractFiles — hashed before the worker runs, refused as a write target, re-hashed afterwards, so a change voids the verdict — and a unit that writes files without verifying reports UNVERIFIED. A whole-file emission may create a file and may not modify one. Then coherenceVerification runs the project's own command, with the power to void a unit whose own tests passed while the tree did not.

The Workings

The architect may reason but not author. The worker may author but not stray. Neither may execute without consent. And source does not leave the machine.

Every rule here is an instance of that sentence — except rule 8, which is about where source may travel — and every one fails closed: if a rule cannot be evaluated, or the approval service cannot be reached, the answer is no.

#RuleEnforced by
1A credential may not reach the cloudDLP gate: refused, or pinned local
2The architect may not author or delete sourcecode guard: denied, or approval-gated
3The architect may not read back what it delegatedthe settled rule: a file a passing unit left unchanged reads silently, and failed, unverified and since-edited ones ask; delegateReadPolicy: 'allow' relaxes all of it, for every agent, and says so
4The worker may not write outside the workspacecontainment on every emitted path
5The worker's code may not run inside the serversubprocess only; in-process fallback off
6A command the architect proposes may not run uncheckedapproval seam
7A delegated result is a verdict, not a claimfiles written without verification report UNVERIFIED
8Source may not reach the cloudcloud-bound requests carrying fenced source are refused by default; sourceEgress decides

Rule 8 is a heuristic: it looks for fenced blocks with a source language tag of at least three lines, so source pasted without a tag, described in prose, or split across short blocks is not detected. It is also the one rule that can refuse a request you typed yourself.

The enforced invariants

Five boundaries, and each says what it does not cover, because a boundary count is only meaningful if every item is the kind of boundary it claims to be.

#boundaryenforced byfails closed how
1Mutation integritytools/pre-execute blocks cloud writes and shell executionthe planning tier physically cannot write or run on the host
2Egress privacytools/post-execute serves .d.ts stubs via sourceReadEgress: 'declarations'a read is refused if the declaration is absent or older than its source
3aWorkspace rootworkspaceDir + emitAllowlistunconditional — the worker cannot escape the sandbox directory
3bTask unit scopetargetFiles, unitScope: 'enforce'conditional — refused for undeclared paths, but a unit that declares nothing is unrestricted across the workspace
4Flakiness containmentrunVerification runs 3 passes by defaultnon-deterministic drift records UNIT_FLAKY and halts promotion
5Promotion socketscripts/check-promotion.cjsany post-verdict edit flips the exit code to 1

Three of those are qualified and the qualifications matter more than the rows:

  • Row 3b is opt-in. The workspace root is a hard perimeter; the unit boundary exists only when the architect declared one, and nothing forces it to. Confusing the two is how a table reads as five unconditional boundaries when one is a contract property.
  • Row 2 is provenance, not verification. The read is redirected to a declaration the build emitted, and staleness is checked by mtime — evidence that a rebuild happened, defeated by a touch, a checkout, or clock skew. tsc is trusted, not proven.
  • Row 5 binds content; it does not authenticate a person. An OPERATOR_ATTESTED record holds a SHA-256 and two self-reported strings. Editing the file flips the socket to 1, but nothing verifies that the named operator did the reviewing. That is tamper-evidence, not cryptographic non-repudiation — there is no key, no signature, and no third party.

For an enterprise review

Enterprise evaluation of hybrid frameworks usually stalls on structure, not on hardware efficiency or token cost. The recurring objection is that a cloud agent with host tool access relies on prompt instructions ("do not modify out-of-scope files") or reactive approval popups — permission that is a pause rather than a boundary, and an author that grades and commits its own work.

Three things here address that directly, each with its limit stated beside it, because a control whose limits are hidden is the thing enterprise review exists to catch.

1. Separation of duties is mechanical, not instructed. The cloud model is an architectural oracle. It is blocked from file mutation and shell execution at tools/pre-execute — across the fifteen routes in control-bypass.test.cjs, four refused outright and eleven held for approval, none reaching source without a grant. Mutation authority sits with a local worker inside a bounded workspace. The limit: the workspace root is an unconditional boundary, but the per-file targetFiles restriction is conditional on declaration — a unit that declares nothing is unrestricted across the workspace. A policy that forces declaration is a reasonable ask and is not built.

2. Promotion is tamper-evident rather than a vibe check. Git shows commits; it does not show whether a remote model or a certified local unit produced the bytes. The promotion socket binds a file to a SHA-256 registered during on-box consensus (three runs, with non-deterministic drift recorded as UNIT_FLAKY) or explicit human review (OPERATOR_ATTESTED). Any post-verdict edit invalidates it and the socket exits 1. promotion-socket.oracle.mjs covers all twelve judgement cases and runs in CI — a missing verdict branch is the bug it exists to catch, because a fall-through to a generic refusal is indistinguishable from correct strictness. The limits: the registry is per-machine and not in the repository, so the socket is a local pre-commit gate rather than a CI gate; and operator is a self-reported string with no key and no signature. That is tamper-evidence, not cryptographic non-repudiation.

3. Egress is bounded by build provenance. With sourceReadEgress: 'declarations', reads of .ts files are answered with compiler-emitted .d.ts stubs, so the architect gets types and signatures without ingesting implementation bodies. Measured on this repository: zero for/while, if/switch and fs. calls in the served declarations, against 16, 80 and 2 in the sources. The limits: the build is trusted, not verified — staleness is checked by mtime, which a touch, a checkout or clock skew defeats; and the option is off by default and needs a plugin restart.

What an enterprise review will find missing

Stated here rather than discovered in a procurement conversation:

  • The registry is local, unsigned and per-machine. Audit records cannot live only on laptops; they need signing with a real identity and shipping to a central manifest or attestation store.
  • An attestation is not durable. A later delegation touching the same file supersedes the record — correct semantics, since a verdict describes content, but it means a human attestation must be re-established and is not a permanent certificate.
  • Task scoping is opt-in, as above: nothing forces an architect to declare targetFiles.
  • A personal project at 0.2.0. The API is not frozen. The honest limits are documented rather than glossed, which is the trait an evaluation should weigh in its favour given how rare it is.

The proposition is not that this shaves a metered bill — measured, that is worth about fifteen cents. It is that local hardware becomes a boundary with a record behind it, and that the model which specifies the work is provably not the one that writes it.

What this control does not cover

An effectiveness statement is part of a control, not an appendix to it. These are the limits, and they are the reason to read this file rather than the feature list.

  • It prevents writes, not intent. The guard sees tool calls and shell text. Source a model produces in conversation, in a computation, through a second tool path, or through any plugin or MCP server outside this one is invisible to it. This is a scope boundary, not a defect, and it means the control's coverage is this plugin's write paths rather than "the model did not write code".
  • The recorder and the recorded run in the same process. The guard, the worker and the ledger all live inside one DSH process, so the record below is evidence of what happened, not proof that nothing else did. Hashing constrains the worker well; nothing here constrains the recorder. Closing this needs an external sink that the process cannot reach back into, which is outside this plugin.
  • It does not make coding cheaper, more private, or longer. This plugin was originally justified by those three claims. Each was tested and each failed; the measurements are in Retired claims rather than deleted, because a control whose author has published its disconfirming evidence is more useful than one with a clean feature list.
  • The guard is not precise. A shell command naming a directory that contains a delegated file is refused even when it only lists names, and a delete verb combined with a source filename anywhere on the same line is refused even when the target is unrelated. Both fail closed — the cost is that ordinary housekeeping stops working once a delegation has landed. Tracked, not yet fixed.
  • The audit record covers delegation, not everything. The ledger's architect-side counters (totalCloudTokens, architectTurns, totalSpendUSD) are structurally zero: the host events that would feed them do not carry usage. What the metered model read is recorded in a SOURCE_READ trace, which is a log file rather than a tracked record. So the trail answers "what was delegated and how did it resolve" and is silent about the rest.
  • Nothing here is tamper-evident. The registry and ledger are plain local JSON. Anyone with write access to the data directory can edit them.

Retired claims, and why

Every row was measured on this repository's own work. They are kept because a control's credibility comes from what it discloses, and because each one is a trap for the next person building something similar.

claimwhat the measurement said
"Your plan is for thinking, your GPU does the typing"True as a mechanism, and close to worthless as a saving. Across 329 delegations the architect was billed 666,756,560 input tokens; the local worker's entire output was 548,106 — 0.08% of the bill. Delegation cannot meaningfully reduce a total it contributes 0.08% to.
"Runs on your machine, so it's private"Refuted. The architect must read source to engineer against it, and everything it reads is re-sent on every later turn — measured at ~8.5 read events per turn. What stays local is the typing, not the knowledge of what is being typed.
"Local routing saves money"Refuted, and never claimed at the bottom of the file. The delegated output is worth $0.1047 of cloud-equivalent; the hardware payback is roughly 19,000× this workload.
"Capacity, so the plan lasts"Unsupported. Session length is set by the context window filling, and that is dominated by the architect's reading, which the plugin does not reduce. Measured evidence that it extends sessions: none.
A third tier would read the repository locally and keep the architect out of the codeBuilt, measured, retired. The tier's window did not fit; a per-model reasoning setting could not be overridden; offloading typing recovered far less than the reading it gave back; no lead-authored contract was ever dispatched and verified. The split of src/index.ts has since retired the first of those four, so the decision rests on the other three. See [docs/ROADMAP.md](doc