Documentation · Visp Code
Visp Code docs.
A harness for AI coding agents. It runs the checks, bounds the file scope, and brings in an independent reviewer, so “done” rests on what was executed rather than what the agent said.
npm install -g visp-coder@beta
How the loop works
Visp Code runs an actor–critic loop around your coding agent’s work.
The agent (the actor) builds one usable slice at a time inside an authorized file scope. Visp Code executes the checks that slice declares, and an independent reviewer (the critic) judges the result against the original request. Its findings come back to the agent as the next repair step.
It works as a CLI (visp) and as an MCP server (visp serve --mcp); both use the same workflow services. In practice the agent runs the commands itself: the installed instructions and hooks tell it how, and send it back when it stops early.
- 1Feature
visp featureKeep the request verbatimVisp Code - 2Work
visp workAuthorize one slice: scope, checks, contextVisp Code - 3ImplementEdit inside the authorized files onlyAgent
- 4Done
visp doneRun the declared checksVisp Code - 5ReviewJudge the result against the requestCritic
- 6Accept
visp acceptRecheck the assembled productVisp Code - 7Hand off
visp prThe reviewer documentYou
- Start from the real request.
visp featurekeeps the request verbatim. Under Claude Code and Codex it takes it from the user’s recorded prompt, so a paraphrase can’t replace it. With the Codex reviewer configured, an independent tester starts writing acceptance tests from the request alone. - Declare a check before editing.
visp workrefuses a slice with a functional outcome that no declared check exercises. For a small change,visp work --check "npm test"works the whole request as one slice. - Stay in scope. The agent edits only inside the slice’s files; hooks and
visp guardenforce it. Its own account of the work is never evidence. - Let the evidence decide.
visp doneruns the declared checks itself: commands without a shell, and browser journeys in an isolated Chrome with screenshots. - Review it independently. When every check passes, a separate read-only reviewer judges the work against the verbatim request. It sees the request, current source, results and screenshots, but not the agent’s verdicts or history.
- Check the assembled product.
visp acceptreruns every check against the whole product, including the pinned independent tests, and needs a current assessment of every mandatory outcome. - Hand a person the evidence.
visp prcollects the request, outcomes, executed results, independent tests, findings and intent changes into a Markdown document for the pull request. It publishes nothing.
visp pr once the review budget (three reviews by default) is spent.What counts as evidence
Only what Visp Code executed itself: check commands, browser journeys and their screenshots, and the reviewer’s attributed judgments. The agent’s own claims are never evidence.
One execution, and what it is bound to
- Product sourceThe files the check depends on
- Slice contractIts outcomes, scope and checks
- Verifier filesThe assertions and their fixtures
- EnvironmentWhere the check ran
- Visp Code runtimeThe version that ran it
C001 · node --test test/save.test.mjs
Bound to all five, and all five are unchanged. This result counts as evidence.
Does this result count as evidence?
- passed, current
- Ran on the current source, contract, verifier and environment. Yes
- failed
- Stays unresolved until the same check passes on the repaired product. No
- stale
- Something it was bound to has changed since it ran. No
- missing
- Never ran, or its result is unavailable. Missing evidence never becomes a pass. No
- environment-failed
- A missing browser, or a sandbox that denies sockets. Nothing about the product was tested. No
- syntax check
- Commands such as
node --checkstill run, but a check has to exercise behavior. No
User guide
From install to a checked change, in four steps.
1. Install and set up
Visp Code needs Node.js 22.16 or newer and a Git repository. It is in beta; the npm package is visp-coder.
npm install -g visp-coder@beta
The package installs three executables: visp, visp-migrate for backed-up upgrades of saved history, and visp-runner, an optional experiment runner. Then set up the project for your coding host: claude-code, codex, cursor, copilot, opencode or generic.
visp init --harness codex
visp install --harness codex --dry-run # preview what will be written
visp install --harness codex
visp doctor
init writes visp.yml and .visp/. install writes the host’s instruction files and VISP.commands.md, registers the MCP server, and installs hooks. visp doctor checks the installation.
| Host | What install adds |
|---|---|
| Every host | A Git pre-commit scope check, instruction files and MCP registration. |
| Claude Code | Edit, shell, prompt and Stop hooks in .claude/settings.json, and a reviewer agent in .claude/agents/visp-critic.md. |
| Codex | Prompt, shell and Stop hooks in .codex/hooks.json, and a reviewer agent in .codex/agents/visp-critic.toml. Codex runs the hooks after you trust them once with /hooks. |
| Cursor, Copilot | A reviewer agent definition under .cursor/agents/ or .github/agents/. |
2. Quick start
visp feature "Add a retry button to the failed-save banner"
visp work --check "npm test" # one slice for the whole request, checked by your tests
# ...implement...
visp done # run checks; independent review
visp next # findings or the next step
visp accept # check the assembled product
visp pr # reviewer document for the pull request
This is the light path: the whole request becomes one slice with scope ** and your test command as its check. Use a full brief when the request has several independently usable parts.
3. Write a brief for larger work
A brief lives at .visp/features/<id>/brief.yaml. It holds outcomes, behavior examples, decisions, checks and slices. The agent authors only the brief; everything else under .visp/ is generated. A slice needs an outcome, a bounded write scope and at least one runnable check for its functional outcomes.
Slice
Checks
Outcome
Select an item to see what it connects to, and where it sits in brief.yaml below.
outcomes:
- id: O001
kind: functional
statement: Starting the game switches it to the playing state.
priority: must
checks:
- id: C001
command: [node, --test, test/game.test.mjs]
outcomes: [O001]
files: [src/game.js, test/game.test.mjs]
verifierFiles: [test/game.test.mjs]
environment: node
- id: C002
command:
kind: browser-journey
journey:
url: http://localhost:3000/
viewport: { width: 390, height: 844 }
actions:
- { kind: click, selector: '#start', capture: true }
- kind: wait-for
selector: '#game'
attribute: { name: data-state, value: playing }
timeoutMs: 5000
capture: true
outcomes: [O001]
files: [index.html, src/game.js]
environment: browser
slices:
- id: T001
goal: Start a game from the title screen.
outcomes: [O001]
scope:
allowed: [index.html, src/game.js, test/game.test.mjs]
checks: [C001, C002]
Update a brief with visp brief --patch - --reason "<why>", which merges entries by id, or replace it with --from -. Common natural field names (description, then, slice IDs like S1) are rewritten and reported; anything ambiguous is refused with its field path.
How checks run
Commands run as an argument vector, never through a shell. A string is split into arguments, and shell syntax such as &&, pipes or VAR=value prefixes is refused; use two checks or a script your project owns.
Browser journeys drive an installed Chrome or Chromium with an isolated profile and record operations, measurements and screenshots. Visp Code never downloads a browser or uses your open session. An HTTP(S) app must already be running.
files lists what the check depends on, so changes make earlier results stale. verifierFiles lists the assertion program and its fixtures; they are hashed separately, so a repair is compared against the same verifier.
4. Scope and host hooks
scope.allowed is the set of paths the agent may change while a slice is authorized. Changing scope takes a brief update and a new visp work.
- Before a writeEdit hook
Claude Code · Refuses out-of-scope writes before they happen, refuses agent edits under
.visp/, and refuses shell commands that would delete Visp state or pinned tests.Codex · Codex edits through
apply_patch, which its hooks don’t intercept. The Git hook andvisp doneenforce edit scope instead.Other hosts · No edit hook is installed. The Git hook and the CI job check scope.
ActiveNot intercepted - Before a commitGit pre-commit
Checks changed files against the authorized scope. It can be bypassed with
Every host--no-verify. - On the pull requestCI job
Checks the pull request diff against the slice scopes in the committed brief, pinned to the Visp Code version that generated it.
Optional · authoritative
Never writable: workflow.blockedPaths in visp.yml, by default .env, node_modules, dist, build and .git, whatever a slice says.
visp guard as a change moves toward review. The CI job is optional (visp install --hooks ci); because the Git hook can be bypassed, CI is the authoritative check.A Stop hook also sends the agent back to an unfinished, recently active feature, at most three times.
Full reference: workflow guide, browser journeys and product review, and internals.
Independent review
The critic is a separate model session that judges the work, not the agent’s account of it.
The reviewer sees
- The original request, verbatim
- The brief’s outcomes
- The current source
- Check results and screenshots
It never sees
- The agent’s verdicts
- Earlier approvals
- The workflow history
| Launch mode | Who starts the reviewer | Needs |
|---|---|---|
codex-exec | Visp Code runs one ephemeral, read-only codex exec session after every check passes, and returns its findings from visp done. Written by visp init --harness codex. | The Codex CLI, signed in, whichever host does the coding |
claude-exec Next beta | Visp Code runs one restricted, read-only claude -p session per review (Read, Grep and Glob only, no hooks or MCP servers). Set it by hand with critic.harness: claude-code. | The Claude Code CLI, signed in; visp doctor checks both |
host (default) | Visp Code prepares a review packet; the coding host delegates it to its own reviewer subagent and submits the reply. | A host that follows the preflight, prepare and submit protocol |
Independent acceptance tests
With codex-exec (and, from the next beta, claude-exec), visp feature also starts a tester that writes one standard-library test file from the original request alone. Visp Code keeps it only if it has at least three assertions and fails on the unimplemented project, then pins it under acceptance/<feature>/. From then on the agent can’t quietly change it: changing it is an intent change that visp pr lists.
The tester runs on new projects only by default (fewer than three tracked source files). On existing codebases, tests written from reading the code assumed behavior the code doesn’t have, and agents then broke correct code to satisfy them. critic.existingCodeTests: true is an experimental, opt-in mode that runs the tester against a disposable copy of the repository.
Budgets
Per feature. Three by default; maxCalls allows up to six.
Per review call.
Ceiling on total review time for the feature.
visp next routes to visp pr. Exhaustion never approves anything.With webSearch: true the reviewer may search the web for public documentation. Every query is logged to the feature and listed by visp pr.
Full reference: critic guide.
Configuration
visp init writes a commented visp.yml at the project root.
Every key is optional, unknown keys are rejected with their path, and the file is never rewritten for you. The keys you are most likely to change:
harness: claude-code # claude-code, opencode, codex, copilot, cursor, generic
profile: minimal # minimal or standard
critic:
harness: codex
launch: codex-exec # or host (default)
maxCalls: 3
workflow:
strictness: standard # relaxed, standard, strict, locked
maxChangedFiles: 40
blockedPaths: [".env", ".env.*", node_modules, dist, build, .git]
validationCommands: # one command per entry, no shell
- pnpm test
context:
tokenBudget: 12000
visp doctor --settings shows every effective value and whether it came from the file or a default. Rerun visp install after changing harness or profile.
What to commit
Most of .visp/ is meant to be committed. A reviewer or CI needs to see what a change declared and what was checked.
Commit
- Briefs
- Generated state
- Screenshots
- Review packets
- Policy and overrides
Git-ignored by init
- Slice authorizations
- The repository index
- Telemetry
.visp/. The machine-local parts stay out of Git, and telemetry never leaves your machine.Full reference: configuration and migrating from 0.4.
Commands
Grouped by when you reach for them.
| Command | Purpose |
|---|---|
| Set up | |
visp init --harness <host> | Create visp.yml and .visp/ |
visp install | Install host instructions, MCP registration and scope hooks |
visp doctor | Check the setup; --settings explains effective configuration |
| The loop | |
visp feature "<request>" | Start a feature from the original request |
visp brief | Read (--template) or update (--patch -, --from -) the brief |
visp work | Authorize a slice and deliver its context; --inspect reads without authorizing |
visp verify | Run the slice’s checks without closing it |
visp done | Run checks, start the independent review, close the slice when resolved |
visp next · visp status | Read-only: the next action · outcomes, progress and open findings |
visp accept | Check the assembled product against its mandatory outcomes |
visp pr | Print the reviewer document |
| Evidence and review | |
visp capture --from - | Run an ad hoc browser journey; --replay <run-id> reruns one |
visp critic | Configure and run the independent reviewer; critic feedback asks the user |
visp review | Prepare or submit a review session against current evidence |
visp observations | List an outcome’s captured output and image paths |
visp reproduce | Link a failed execution to an open finding before repair |
visp guard | Check changed files against the authorized scope |
| Project knowledge | |
visp index · visp query | Build and query the repository graph |
visp learn · visp recall | Record and recall project notes |
visp skill | Propose, admit and retire project skills; skill seed edge-cases-first adds the bundled testing skill |
visp rules Next beta | List or remove the rules the user stated for all later work |
visp trail prune Next beta | Remove local captures and candidates that retained history no longer references |
CLI output is compact; --json prints the full result. MCP tools reply with compact text plus the complete result in structuredContent; pass detail: true for full text.
In the next beta
On the main branch now, not yet on npm. npm install -g visp-coder@beta installs 0.5.0-beta.2 until the next release.
- A Claude reviewer
critic.launch: claude-execwithcritic.harness: claude-coderuns the independent reviewer and tester as restrictedclaude -psessions, whichever host does the coding.- Flip check
workflow.flipCheck: autoreruns each passing check on the pre-change code in a temporary copy. A check that passes either way is shown as a coverage gap; it never gates acceptance. Useofffor checks with external side effects.- Dispute a wrong test
visp done --dispute "<test name>" --reason "<request quote and why>"asks the independent reviewer to rule on a pinned test. It is the worker’s only route against one.- Existing tests protected
- Workers are pointed at up to eight existing tests that cover what they changed, and reviewers must report a weakened, skipped or deleted test unless the request changes that behavior.
- Large repositories
- Reviews of large existing codebases anchor on the files changed since authorization and get a bounded diff first.
visp initadopts a whole-project check only if it passes now. - Static pages and rules
- Browser journeys can open
project:/index.htmlwith no server running.visp ruleskeeps the rules you state for all later work.
Details: changelog, Unreleased and the critic guide.
Results & limits
On Visp Code’s own benchmark, it matched or beat every other arm on every task, most clearly on the larger ones.
Four tasks with hidden checks, Claude Haiku 4.5 as the coding agent, three runs per arm. The other arms were bare coding, Spec Kit and BMAD.
Every run, with wall time
| Task (hidden checks) | Visp Code | Bare | Spec Kit | BMAD |
|---|---|---|---|---|
| Reservations API (44) | 44, 44, 44 8–10 min | 38, 39, 40 2–4 min | 39, 40, 37 10–13 min | 38, 38, 39 3–6 min |
| Spreadsheet engine (38) | 36, 35, 37 10–16 min | 28, 32, 26 5–8 min | 28, 28, 27 12–15 min | 33, 32, 35 3–22 min |
| Bundles on an existing service (60) | 60, 60, 60 5–9 min | 60, 60, 60 2–3 min | 60, 59, 59 8–11 min | 60, 60, 60 3–5 min |
| Extending an existing engine (63) | 57, 62, 63 14–16 min | 51, 53, 53 9–11 min | 56, 59, 41 11–13 min | 56, 51, 50 7–13 min |
With a strong worker (October)
With Codex gpt-6-luna at maximum effort as the worker, every arm scored at or near the maximum on the four new-project tasks, so they no longer separate the arms; Visp Code took about 22 minutes per task and Spec Kit about twice that. On existing repositories, SWE-bench Verified Mini (50 Django and Sphinx issues, two runs per arm) with a Claude Sonnet 5.5 reviewer through claude-exec:
| Arm | Resolved, runs 1 and 2 | Of 100 | Mean time per task |
|---|---|---|---|
| Visp Code | 37, 32 | 69 | 8.3 min |
| Bare | 32, 34 | 66 | 3.5 min |
| Spec Kit | 30, 34 | 64 | 10.8 min |
| BMAD | 33, 29 | 62 | 6.2 min |
None of these differences is significant (sign tests, p 0.12 to 0.58). Strong workers gain little on these tasks; the clearer gains are with a weaker worker, above.
Every number and its limits: research summary. The harness, tasks and hidden checks are in bench/.
When something is stuck
worksays the slice has no runnable check- Declare the command that exercises the slice (your test runner or a browser journey). The reply includes a patch that adds one. For a small change, use
visp work --check "<test command>". - A check failed with
environment-failed - Nothing about the product was tested. Install the missing executable or browser, or rerun the same command with your host’s sandbox escalation, then pass
--retry-environment. - No review happened after
done - The reply says why. For
codex-exec, check thatcritic.harnessiscodexand the Codex CLI is signed in. From a sandboxed shell without network, rerunvisp donewith sandbox escalation. - An edit or commit was refused for scope
- The error names the out-of-scope files. Update the brief and run
visp workagain rather than widening scope by hand. A denial that blames the installation is fixed withvisp doctor. STATE_BUSY, or a mismatch after upgrading- Another Visp Code writer is active;
visp doctorreports lock owners and--fixrecovers abandoned transactions. After upgrading, rerunvisp installand restart MCP servers and host sessions. Projects with 0.4 history needvisp-migrate.
Source, issues and changelog: github.com/djkeshawa/visp-coder. Checked against release 0.5.0-beta.2 and the main branch on October 11, 2026; features marked “Next beta” are on main but not yet on npm. Use visp --help for your installed version.
