/loopspec — informal intent → harness-driven delegation specification
Takes everything the user wrote before the literal token /loopspec and compiles it
into a machine-checkable delegation contract for a DeepSeek worker. The contract's
defining property: the harness, not the model, decides when the task is done.
1. Trigger & parsing
Input syntax: <informal task description> /loopspec [flags]
- Everything preceding
/loopspecis the requirement source. Text after it is flags only. - If
/loopspecappears with nothing before it in the current message, use the most recent substantive user request in the conversation as the requirement source. - Never ask clarifying questions before emitting the spec. Ambiguity that survives is recorded verbatim under Open Bindings in §1 of the output and given a stated default.
Flags
| Flag | Effect |
|---|---|
--dispatch |
After emitting the spec, hand it to a DeepSeek worker (see §7). Default: emit only. |
--max-iter=N |
Hard iteration ceiling in the convergence loop. Default 6. |
--lang=py|ts|rs|go |
Force the toolchain instead of detecting it. |
--f2p=<path> |
Name the fail-to-pass test target explicitly. |
--dry |
Emit the spec with detected commands but do not run anything, not even detection. |
2. Pre-compilation probe (mandatory, cheap, read-only)
The commands in §3 of the output must be real commands for this repo, never placeholders. Before emitting, probe the cwd — one batched call, read-only:
ls pyproject.toml setup.cfg package.json Cargo.toml go.mod Makefile 2>/dev/null
sed -n '1,40p' pyproject.toml 2>/dev/null | grep -iE 'ruff|black|mypy|pytest|flake8'
jq -r '.scripts // {} | to_entries[] | "\(.key): \(.value)"' package.json 2>/dev/null
ls tests test spec __tests__ 2>/dev/null
git -C . rev-parse --abbrev-ref HEAD 2>/dev/null
Resolution order for each command slot:
- An explicit script/target in the project manifest (
package.jsonscripts,Makefiletarget,[tool.*]section) — always wins. - The tool the manifest declares as a dependency.
- The language default (
ruff check ./pytest -q,eslint ./vitest run,cargo clippy -- -D warnings/cargo test,golangci-lint run/go test ./...).
If nothing resolves, emit MISSING: <slot> in that slot and list it as a blocking
Open Binding. Never invent a command that was not observed.
deepseek-harness is the wrapper the worker runs the sequence under. If the repo has no
harness entry point, the harness is the ordered command sequence plus its exit codes —
say so in the spec rather than referencing a binary that does not exist here.
2b. Command validation (mandatory before emission)
A resolved command is not yet a valid one. Every string that will appear in §3 of the output must be proven to exist and to parse, in the target project, before the spec ships. Cheap, non-mutating, no test actually executed:
<linter> --version # tool installed and on PATH
<linter> --help >/dev/null # invocation form accepted by this version
<test runner> --collect-only -q <F2P target> # pytest: target path resolves and imports
<test runner> --listTests <F2P target> # jest/vitest equivalent
<harness entry> --help # harness binary exists, if one is referenced
Rules:
- Pin the interpreter, never trust PATH. Resolve the project's own environment
(
.venv/bin/python -m pytest,node_modules/.bin/vitest,poetry run,uv run) and emit that. A second installation of the same tool on PATH is the normal case, not the exception, and it usually has a different version and a different dependency set — the PATH copy then dies at import time with an error that reads like a code defect and is not. - A command whose tool is not installed is
MISSING, not a suggestion to install it. Declared in the manifest's dev extras but absent from the environment still counts as absent: check the disk, not the declaration. - Read the collected count, never the probe's exit code. Verified on pytest 8:
--collect-only -qon a file with no tests printsno tests collectedand exits 0. Gating on$?blesses a target that can never fail, and every termination gate then passes vacuously against an empty diff. Require a printed count>= 1; anything else is a broken F2P binding and a blocking Open Binding. Same trap in jest (--listTestson a non-matching pattern) andgo test ./...on a package with no_test.go. - Flags must match the installed version, not the one you remember.
--collect-only,-D warnings,--listTestsand friends differ across major versions; the--helpprobe is what settles it. - Quote and escape exactly as the worker will paste it. A path with a space, a glob the
shell would expand, a
-kexpression with parentheses — all belong inside quotes in the emitted string, not left for the worker to fix. - No command chaining in a §3 slot. One slot, one invocation, one exit code —
&&collapses two failure modes into one and destroys the gate's diagnostic value.
2c. Lint baseline — the gate is a delta, never an absolute
linter_errors == 0 is unsatisfiable on almost every real repo, and a spec that demands it
forces the worker to choose between violating the scope limit and never terminating. Measured
on a live 722-test project: 1232 pre-existing ruff errors repo-wide, 25 in the single target
file. Neither is the worker's to fix, and both would be attributed to its diff.
So capture a lint baseline before the first EDIT and gate on the difference:
<linter> check --output-format=json <in-scope files> \
| python3 -c "import json,sys,collections;print(dict(collections.Counter(x['code'] for x in json.load(sys.stdin))))"
Compare the post-edit fingerprint against the baseline as a multiset of (file, code),
not as a total and not by line number — lines shift under any edit, codes do not. The gate is:
no code absent at baseline appears afterwards, and no code's count rises. A drop is fine
and needs no comment.
Scope the linter invocation to the §1 writable files. Linting the whole repo imports 1200 diagnostics the diff never touched into a gate the diff is judged by.
Emit this in the spec as an explicit gate restatement, with the baseline fingerprint inline so the worker can evaluate it without re-deriving anything.
3. Output template — emit verbatim, filling every bracket
# DELEGATION SPECIFICATION: HARNESS-DRIVEN VALIDATION LOOP
## 1. TARGET GOAL
- **Functional Objective:** [precise, unambiguous, testable requirements]
- **Target Files / Scope:** [explicit writable paths; everything else read-only]
- **Open Bindings:** [ambiguity + the default assumed; "none" if none]
## 2. HARNESS ENVIRONMENT & GROUND TRUTH
- **Harness Interface:** [harness entry point, or the ordered command sequence] is the
sole deterministic oracle of task status.
- **Fail-to-Pass (F2P) Criteria:** [test target defining the feature/bugfix — must fail now, pass at the end]
- **Pass-to-Pass (P2P) Criteria:** [existing suite guarding regression — must stay green]
- **Test Integrity Constraint:** Modifying, mocking, skipping, xfailing or otherwise
tampering with harness test suites or oracle fixtures is prohibited and invalidates the run.
## 3. VERIFICATION COMMANDS
1. Lint & Static Analysis: `[exact command]`
2. Harness Patch Evaluation: `[exact command]`
3. Targeted Test Execution: `[exact command]`
## 4. CONVERGENCE LOOP (FORMAL EXECUTION PROTOCOL)
Iterate until termination criteria hold, ceiling [N] iterations:
1. **EDIT:** apply scoped modifications to in-scope source files.
2. **EXECUTE:** run the §3 sequence in order, in the sandbox.
3. **PARSE:** consume the structured payload — failing assertions, stack traces, linter records.
4. **PATCH:** derive root cause from the parsed diagnostics and refine the edit.
On ceiling without convergence: stop, do not report success, return the last diff plus the
unresolved failure set.
## 5. TERMINATION CRITERIA (BOOLEAN GATES)
Finalize IF AND ONLY IF all gates hold, each backed by captured stdout:
- [ ] `harness_exit_code == 0`
- [ ] `fail_to_pass_status == ALL_PASSED`
- [ ] `pass_to_pass_regressions == 0`
- [ ] `new_linter_diagnostics == 0` — post-edit `(file, code)` multiset over the §1
writable files introduces no code absent from the baseline and raises no count.
Baseline fingerprint: `[inline it here]`. Absolute zero is NOT the gate; the
repo's pre-existing diagnostics are out of scope and fixing them is a scope violation.
## 6. GUARDRAILS & EXECUTION CONSTRAINTS
- **Zero-Assumption Rule:** completion is never declared without verifiable stdout and exit codes.
- **Context Preservation:** no raw log dumps; extract diagnostic diffs and failure traces only.
- **Blast Radius Limitation:** minimal diffs, strictly inside the §1 scope; no drive-by refactors,
no dependency additions, no reformatting of untouched lines.
- **Oracle Supremacy:** the harness verdict is final and overrides the worker's own
judgement. A test the worker believes is wrong is still the specification; it reports
the disagreement and stops, it does not edit the test to agree with its code.
- **Baseline Obligation:** before the first EDIT, run §3 once and record the baseline —
a P2P test already red at baseline is not a regression you caused; report it, do not fix it silently.
4. Compilation rules
- Objective: rewrite intent as an observable postcondition. "make the parser handle
nested quotes" → "
parse()returns the unescaped inner string for inputa "b \"c\"" dinstead of raisingValueError." - Scope: name files. A scope of "the codebase" defeats the whole gate; if the target file is genuinely unknown, that is an Open Binding and the first loop iteration is a read-only localisation pass.
- F2P: if no failing test exists yet, the F2P criterion is "author
<path>::<test_name>first, confirm it fails against unmodified source, then implement." Test-first is the only way the oracle can distinguish a fix from a no-op. - P2P: the pre-existing suite, minus anything red at baseline.
- Test directories are read-only except the single F2P test file when the F2P criterion is authoring it. State that exception explicitly; leave nothing to inference.
5. Invariants
The spec is invalid, and must be regenerated rather than shipped, if any holds:
- a §3 command is a placeholder, a guess, or
MISSING; - a §3 command was never put through the §2b validation probes;
- the F2P target collects zero tests (the gate would pass vacuously);
- F2P and P2P name the same target (nothing then guards regression);
- scope includes a test path without the §4 authoring exception;
- a termination gate is unmeasurable by the §3 commands as written;
- the lint gate demands absolute zero on a repo whose baseline is non-zero.
6. Emission
Write the spec to .claude/loopspec/<slug>.spec.md (create the directory), print the path plus a
three-line summary: objective, scope, gate count. The spec file is the artefact handed to the
worker — the conversation carries the pointer, not the payload.
7. Dispatch (--dispatch only)
Hand the spec file to your DeepSeek runner (e.g. deepseek-tui, or any non-interactive coding
agent CLI), one worker, in a git worktree so the diff stays reviewable:
git worktree add -b loopspec/<slug> .worktrees/loopspec/<slug>
deepseek exec --auto --yolo --cd .worktrees/loopspec/<slug> \
"$(cat .claude/loopspec/<slug>.spec.md)"
The worker never sees this conversation, so the spec file must be self-contained — that is the point of writing it to disk first.
A worktree has no environment. git worktree add copies tracked files only, so the
project venv / node_modules is absent and every §3 command fails on a fresh worktree.
Before dispatching: link it (ln -s ../../.venv .venv) and then prove the source resolves
inside the worktree, because an editable install points at the main checkout and a worker
can otherwise spend its whole budget editing files that the tests never import:
.venv/bin/python -c "import <pkg>; print(<pkg>.__file__)" # must be under the worktree
.venv/bin/python -m pytest -q # baseline must reproduce here, not just in the main tree
Copy the spec into the worktree too — the worker reads it from its own cwd.
After the worker returns, re-run every gate yourself. A worker's claim of success is evidence, not proof. In order:
git status --porcelainandgit diff --stat— is the change surface the §1 scope?git diff -- tests/ pyproject.toml— must be empty apart from the F2P file. A green report next to a modified test or a loosened warning filter is a failed run.- Re-run §3 slots 2 and 3. Compare the pass count against the baseline, not against zero.
- Recompute the lint fingerprint and diff it against the baseline multiset.
- Prove the F2P test was really red. This is the one gate a worker cannot self-certify,
and the one most worth checking: stash the source fix and run the F2P target alone.
It must FAIL. If it passes without the fix, the test asserts nothing and every other gate is meaningless.git stash push -q -- <source files> && .venv/bin/python -m pytest -q <f2p target>; git stash pop -q