dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
IronLaw: evidence-backed completion and repair layer for coding agents
Turn tens of thousands of hard-won lessons from real engineering sessions into delivery discipline, enforced outside the coding assistant.
One host-agnostic layer for every coding assistant; it only cares about three things: less rework, no drift, no reward-hacked fake "done".
IronLaw is not a new coding assistant, and it is not a re-implementation of OpenCode. It is an efficiency-engineering component that sits outside the coding assistant:
DeepSeek Harness / OpenCode / Claude / Grok / Zcode / Codex / Qoder / other thin-shell assistants
│
└── IronLaw layer: task constraints, factual evidence, completion gate, bounded repair
It starts from failure patterns that recur across tens of thousands of engineering sessions: the model is usually capable, but often lacks an external mechanism that keeps constraining it through a long task. IronLaw does not try to turn one model into another; it turns the task process into a verifiable engineering process.
Users keep their original GUI, provider, model, tools, and workspace. IronLaw does not require switching workbenches or understanding MCP; the launch shape is a unified CLI installer, a unified sidecar kernel, and thin per-host plugin/hook adapters.
npx @ironlaw/cli install --host opencode
After install, the user keeps using the host normally. IronLaw records facts, checks task state, and reminds, blocks, or requests a minimal repair when needed. What is described here is an observable mechanism, not a guarantee about any model, host, or task outcome.
To understand IronLaw's integration shape, first separate the responsibilities of Router, Skill, MCP, and Plugin:
User input
│
▼
Router: choose Agent / Provider / Model / Skill
│
▼
Agent + Skill: understand the task, plan steps, propose tool calls
│
▼
OpenCode Runtime: actually executes tools, writes files, runs commands, ends the session
│
└──── IronLaw Plugin: observe, constrain, audit, repair
│
▼
ironlawd sidecar
A Skill is good at freezing "how it should be done": checklists, code style, a framework's workflow, prompt templates for a class of tasks. A Router can select and load a Skill for the agent by task.
But a Skill still lives inside the model context:
So IronLaw's rules can be borrowed by a Skill, but IronLaw itself must not ship as a Skill. A Skill is the advice layer; IronLaw solves process control and delivery adjudication.
MCP is good at giving the model search, databases, external APIs, or human-query capabilities. The model sees the tool schema and decides whether to call it.
That is not the right main path for IronLaw:
So MCP can serve as a future human-query interface — viewing reports, approving pending actions — but it cannot carry IronLaw's completion gate.
A Plugin runs inside OpenCode's host lifecycle and can see the messages, tools, permissions, files, and session events that actually happen after the Router. It can:
So the relationship is:
Router decides who gets the task, with what model and Skill
Skill decides what method the model should use
MCP decides which external capabilities the model may call
Plugin decides what actually happened on the host, which actions may continue, and whether it really finished
IronLaw does not fight Router for routing, Skill for methodology, or MCP for the tool ecosystem. It fills the delivery-control surface none of the three own: less rework, no drift, and catching reward-hacked fake completion. This describes the plugin's mechanism and observation boundary, not an outcome guarantee.
Make every execution round aim at the final delivery, instead of first building a "looks like it runs" minimal frame and then having the user backfill spec, tests, build, and deployment. IronLaw uses a Task Contract, requirement-evidence mapping, and delivery-chain checks to surface rework risk as early as possible.
Make the model return to the original spec after context compaction, long execution, and handoffs, instead of treating an adjacent problem as the new goal. IronLaw keeps the original requirements, constraints, and allowed scope, and corrects with a short anchor when drift is detected.
Natural-language claims like "tests passed", "build done", or "already fixed" are not accepted as delivery facts. IronLaw requires independent evidence from tool events, exit codes, file changes, workspace fingerprints, real builds, and the final journey. When evidence is insufficient, the task can only be unverified, pending-repair, or failed — never "done".
A test command exiting 0 only means one command ended successfully. It does not prove:
This is exactly where reward hacking happens: the model optimizes "make the current check pass" without delivering what the user actually wants. IronLaw judges "test passed" and "delivery complete" separately.
The model tends to pick the shortest-looking path: write a minimal frame first, get the tests green first, produce an intermediate artifact first. That looks efficient in the short term, but may drop constraints, move boundaries, or bypass the final user journey — and the user ends up re-specifying, re-testing, and re-packaging.
IronLaw does not forbid a reasonable minimal implementation; it requires the implementation path to keep mapping to the original spec and final acceptance items. An action that does not shrink the delivery gap is not valid progress. The point is not to make the model write more code, but to reduce the "finished one round, tear it down, redo it" rework.
After context compaction, long tool runs, and several handoffs, the model may forget the original goal and start solving an adjacent problem nobody asked for. The model's todo, handoff, or "I remember the user wanted…" cannot replace the original task.
IronLaw keeps the original task contract and injects a very short task anchor only when actually needed — not repeating the whole history every round. The anchor's job is to hold the boundary, not to stuff a new long prompt back into the model.
"Tests passed", "build completed", "files updated" are just model text. If the event stream has no matching tool call, exit code, file change, or artifact hash, these are only claims awaiting verification — not completion evidence. This is IronLaw's direct defense against reward hacking: the model may report, but it cannot issue its own delivery certificate.
Without an external completion boundary, the model may keep reasoning, re-editing, or casually expanding scope after reporting, spending tokens without adding delivery value.
IronLaw turns continuation into a conditional, counted, must-make-progress repair action; with no new evidence or no hard gap closed, auto-continuation stops.
| What the user sees | Mechanism behind it | What it mainly prevents |
|---|---|---|
| Remembering what the task really asked | Task Contract, original-input hash, task anchor | spec drift, forgetting the goal after compaction, handoff rewriting the requirement |
| Knowing whether the model actually did it | Event Ledger, tool events, exit codes, file and Git facts | self-reported completion, fabricated tests, imaginary commands |
| Not mistaking a green light for delivery | CompletionGate, requirement-evidence mapping, delivery-chain check | test passed but not shippable, build missing artifacts, only intermediate files made |
| Stopping dangerous actions first | deterministic Policy Engine, workspace-boundary checks | out-of-scope deletion, dangerous Git operations, unauthorized publish |
| Detecting drift or spinning | Drift Score, progress detection, gap-delta comparison | long-range deviation, repeated edits, useless loops |
| Repairing only the smallest gap when continuing | bounded Repair Loop, repair fingerprint, budget cap | endless "one more thing", infinite continuation, useless retries |
| A broken plugin doesn't take down the host | sidecar watchdog, capability handshake, degradation strategy | plugin failure making OpenCode unusable |
IronLaw does not have another large model review the worker every round. It puts a single-agent task into a finite state machine outside the model:
OBSERVING
↓ code task recognized
ACTIVE
↓ model stops / claims done
VERIFYING
├─ all hard acceptance has valid evidence → VERIFIED
├─ gap is repairable → REPAIR_REQUIRED
├─ dangerous or out-of-scope action → BLOCKED
└─ budget exhausted / no progress → FAILED_UNVERIFIED
The model cannot write the task directly to VERIFIED; the todo cannot write it directly to VERIFIED; a single command exiting 0 cannot write it directly to VERIFIED. Only the CompletionGate may grant delivery status.
Every evidence record stores its source, time, workspace fingerprint, command summary, and related acceptance items. Once evidence is produced, if the related source file changes again, the old evidence is automatically invalidated.
E0 model natural-language claim of completion
E1 todo / handoff / self-reported file list
E2 OpenCode tool call and its return value
E3 sidecar's independent file, Git, command, and hash checks
E4 sidecar's real test, build, launch, relaunch, and artifact checks
E0 and E1 cannot be upgraded into E3/E4. A typical reward-hack path is: modify the test so it passes, run only the wrong subset, claim a command ran, or only generate a config file. IronLaw breaks these into fact checks instead of accepting the model's summary.
The original task is decomposed into hard acceptance items, prohibitions, allowed scope, and expected evidence. Completion is checked item by item along the delivery chain:
requirement mapping
→ implementation change
→ valid test
→ real build/launch
→ user journey
→ persistence/relaunch
→ final artifact
If any hard link is missing, the state is incomplete or blocked — not "passed with reservations".
IronLaw does not re-inject the whole spec every round. It injects a ~200–400 token task capsule at task establishment, context compaction, evidence expiration, or obvious drift:
[IronLaw task anchor]
Objective: fix login-state loss after refresh.
Open requirements: AC-2 expired-token error; AC-3 real refresh journey.
Constraints: don't swap the auth framework; never claim an unrun test passed.
Current evidence: unit=pass; build=stale; journey=missing.
Completion rule: all hard acceptance must have valid evidence before reporting done.
The drift score only triggers a reminder or a blocking policy; it is not completion evidence. Signals include: edits unrelated to open acceptance items, repeated edits with no new evidence, the goal disappearing after compaction, handoff conflicting with the original spec.
Auto-repair is not a vague "keep trying"; it only sends the current smallest gap:
So IronLaw's goal is not to make every task run more rounds, but to use a small fixed overhead to reduce the manual rework after a whole task fails.
"Subprocess" here only means the plugin sidecar or an OS process the host explicitly starts — not another agent, and not a Leader/Worker seat. The plugin does not create multi-agent setups, assign seats, or dispatch roles. What must be prevented is the same session / same external start request pulling up processes repeatedly, and children outliving a dead parent, accumulating large numbers of Bun/OpenCode processes.
So the unified kernel must treat sidecar/subprocess lifecycle as a P0 problem:
lease_id, parent process, session, start time, and task budget;handoff_id is idempotent and must not start twice;status shows active leases, orphan leases, and cumulative CPU and memory;doctor --workers lists and safely reclaims subprocesses IronLaw itself started;bun name; identify precisely by lease, command summary, and parent-child relation.This machine once accumulated 25 running opencode run --format json --pure subprocesses, roughly 2 GB of memory; that class of incident outranks any new host adapter. Without lifecycle guardrails, the plugin's own runtime would create rework and cost problems.
The OpenCode plugin receives host events, runs fast pre-policy checks, and injects short anchors; the local sidecar owns the state machine, evidence ledger, workspace checks, and completion audit.
OpenCode GUI / Provider / Agent
│
IronLaw Plugin
│ stdio NDJSON
▼
ironlawd sidecar
The launch does not register a dozen governance tools with the model. This avoids a fixed MCP-schema token tax, and avoids handing the "is it done" judgment to the model's voluntary tool calls. MCP may later serve as a human-query or cross-host compatibility interface, but it is not the launch main path.
The iron rules discussed in the Hackathon design are not another longer system prompt; they become executable orchestration algorithms:
| Methodology | Technical expression in the layer | Problem it solves |
|---|---|---|
| VDDG entropy reduction | intent compilation, Task Contract, requirement-evidence mapping | divergent input, vague goal, shortest-path misreading of the requirement |
| boundary conservation | allowed scope, must/must-not, tool pre-policy | task boundary widened, handoff rewriting the original requirement |
| loop control | finite state machine, progress scoring, repair budget, idempotent fingerprint | long-range spinning, repeated edits, endless continuation |
| evidence conservation | event sourcing, evidence grades, workspace hash, stale invalidation | self-reported results passing as facts, stale tests passing as new evidence |
| controlled entropy increase | constrained exploration and candidate comparison | chasing only the shortest path, executing without arguing it through |
The launch unified kernel implements the first four as a single-agent closed loop; controlled entropy increase, model certification, and multi-agent collaboration belong to other components, outside this plugin's committed scope.
The long-term direction is to extract model behavior labels from real engineering sessions — tendency to skip steps, tool-call accuracy, boundary awareness, sycophantic output, long-range stability. Labels are not for scoring or showing off; they let the orchestration layer choose different task granularity, checkpoints, and review intensity.
This layer is called CertifyGate. For now it only keeps the interface and research conclusions; it is not a hidden model-evaluation service in the current plugin, and it does not add extra model calls by default.
The completion gate no longer asks "did the agent call a tool this round". It asks a narrower question: does every applicable acceptance item of the current task have still-valid evidence? A tool call, a model claim, or a summary cannot answer that; only host-observed facts can. The constraint object moves from the turn to the task, and context management is made to follow that task state instead of a fixed rewrite schedule.
The gate is a pure adjudicator with six verdicts (allow_response, verified_complete, repair_required, incomplete, blocked, cancelled), driven by a versioned task contract, an explicit claims_success input, hard-constraint checks, and a bounded repair counter keyed on object/context digests rather than turn ids.
Its trusted boundary is a resolveAudit resolver that assembles the verdict input from durable host events only:
unknown and fails closed.code, docs, ops, research, discussion) and each type must produce its own host-observed artifact: a document write whose target answers the request, a delivered response backed by an external source query whose citations the host can authenticate (a delivery that cites a source the session never reached is refused, and so is one that cites nothing while sources were consulted), an unmasked state-changing entry point, or — for discussion — no artifact at all, with an authorized, provenance-carrying exclusion. No type falls back to another type's evidence, so a passing test suite cannot close a documentation request. Classification reads the human message only: a model calling its work "discussion" cannot reclassify it. An empty or unrecognized request is read as code, the strictest type.host_verifier proof per complete tool.call / tool.result pair, paired by native call id. A half pair, or a model/summary-sourced record, is never proof.config/app.yml is not confused with other/app.yml; a bare name is matched on the basename, and the check reference says so. A prohibition the host cannot resolve or open is not declared satisfied and not declared inapplicable either: it stays unknown and is listed for review, per spec §3, with the reason in the check reference. A prohibition that names no object at all ("不要动代码") is recorded in the contract's unrepresentable_prohibitions rather than turned into a requirement no evidence could ever close. A prohibition is never also a deliverable: an object the request forbids changing cannot be a document it is waiting for. Other types keep a single acceptance item, because a test run covers a repository rather than a named file.exit_code, zero occurrences of the string exitCode, and no card field in any tool-result meta payload (the real meta is a tool's own structured output, such as a file read's lines). The exit code exists once per call in the tools/result hook's canonical value, so the adapter persists a tool.outcome record keyed by native call id and the resolver reads the numeric exit code from there. No numeric exit code plus non-empty command means the evidence stays unknown.echo, ls, cat and reads verify nothing. Masking is judged per shell dialect, taken from the tool name of the paired call: POSIX treats ||, ;, |, & and newline as masking, while PowerShell treats only ;, ||, & and newline as masking, because a PowerShell pipeline preserves the underlying exit code — measured with process exit codes: cmd /c exit 3 | Select-Object -Last 1, cmd /c exit 3 | cat and cmd /c exit 3 | findstr x all still report failure, whereas cmd /c exit 3; Write-Host hi returns 0. A backgrounded verification (npm test &) is masking in both dialects: the connector returns immediately, so its exit status says nothing about the work. An unrecognized tool name gets the POSIX reading, which is the stricter one.Context governance is a companion capability, described in its own section below.
The monorepo suite passes 257 tests (adapter-dsh 238, cli 6, memory 13) with a clean npm run typecheck. The resolver was shaped by six rounds of adversarial probes written and run by reviewers who were not the implementer, plus an audit of a real ledger:
verified_complete for a file change plus any exit-0 command, and an exit-code masking probe showed npm test || true granting verified_complete even when the test failed.echo hello satisfy a deploy, and a local file read satisfy a research request; a secon