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.

  • Node.js 22.16+
  • CLI · MCP
  • Beta · 0.5.0-beta.2
  • Checked Oct 11, 2026
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.

repair · review findings send the slice back to step 3
  1. 1Featurevisp featureKeep the request verbatimVisp Code
  2. 2Workvisp workAuthorize one slice: scope, checks, contextVisp Code
  3. 3ImplementEdit inside the authorized files onlyAgent
  4. 4Donevisp doneRun the declared checksVisp Code
  5. 5ReviewJudge the result against the requestCritic
  6. 6Acceptvisp acceptRecheck the assembled productVisp Code
  7. 7Hand offvisp prThe reviewer documentYou
Visp Code runs itYour agent does itIndependent reviewer
  1. Start from the real request. visp feature keeps 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.
  2. Declare a check before editing. visp work refuses 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.
  3. Stay in scope. The agent edits only inside the slice’s files; hooks and visp guard enforce it. Its own account of the work is never evidence.
  4. Let the evidence decide. visp done runs the declared checks itself: commands without a shell, and browser journeys in an isolated Chrome with screenshots.
  5. 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.
  6. Check the assembled product. visp accept reruns every check against the whole product, including the pinned independent tests, and needs a current assessment of every mandatory outcome.
  7. Hand a person the evidence. visp pr collects the request, outcomes, executed results, independent tests, findings and intent changes into a Markdown document for the pull request. It publishes nothing.
One feature, start to finish. The loop always ends: in acceptance, or in a handoff to 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
Passed C001 · node --test test/save.test.mjs

Bound to all five, and all five are unchanged. This result counts as evidence.

Select an input to change it.
Each result is tied to the product source, the slice contract, the verifier files, the environment and the Visp Code runtime. Change any of them and the affected evidence becomes stale.

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 --check still run, but a check has to exercise behavior.
No
Stale, missing or unavailable evidence keeps an outcome open.

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.

HostWhat install adds
Every hostA Git pre-commit scope check, instruction files and MCP registration.
Claude CodeEdit, shell, prompt and Stop hooks in .claude/settings.json, and a reviewer agent in .claude/agents/visp-critic.md.
CodexPrompt, 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, CopilotA 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

T001Start a game from the title screenMay write: index.html, src/game.js, test/game.test.mjs
declares

Checks

C001node --testtest/game.test.mjs
C002Browser journey390×844 · click #start · wait for playing
exercise

Outcome

O001 · mustStarting the game switches it to the playing stateFunctional
How the example brief below fits together. A slice is authorized only when its checks exercise its functional outcomes.
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.

  1. 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 and visp done enforce edit scope instead.

    Other hosts · No edit hook is installed. The Git hook and the CI job check scope.

    ActiveNot intercepted
  2. Before a commitGit pre-commit

    Checks changed files against the authorized scope. It can be bypassed with --no-verify.

    Every host
  3. 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.

Three checkpoints call 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
ReturnsAn assessment of each outcome, and at most three findings. It runs read-only.
What the reviewer is given. Required findings reopen the slice and go back to the agent as the next repair.
Launch modeWho starts the reviewerNeeds
codex-execVisp 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 betaVisp 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

3reviews

Per feature. Three by default; maxCalls allows up to six.

3min

Per review call.

18min

Ceiling on total review time for the feature.

Every reserved call counts, including failed ones, and nothing retries automatically. When the budget is spent, 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
What lives in .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.

CommandPurpose
Set up
visp init --harness <host>Create visp.yml and .visp/
visp installInstall host instructions, MCP registration and scope hooks
visp doctorCheck the setup; --settings explains effective configuration
The loop
visp feature "<request>"Start a feature from the original request
visp briefRead (--template) or update (--patch -, --from -) the brief
visp workAuthorize a slice and deliver its context; --inspect reads without authorizing
visp verifyRun the slice’s checks without closing it
visp doneRun checks, start the independent review, close the slice when resolved
visp next · visp statusRead-only: the next action · outcomes, progress and open findings
visp acceptCheck the assembled product against its mandatory outcomes
visp prPrint the reviewer document
Evidence and review
visp capture --from -Run an ad hoc browser journey; --replay <run-id> reruns one
visp criticConfigure and run the independent reviewer; critic feedback asks the user
visp reviewPrepare or submit a review session against current evidence
visp observationsList an outcome’s captured output and image paths
visp reproduceLink a failed execution to an open finding before repair
visp guardCheck changed files against the authorized scope
Project knowledge
visp index · visp queryBuild and query the repository graph
visp learn · visp recallRecord and recall project notes
visp skillPropose, admit and retire project skills; skill seed edge-cases-first adds the bundled testing skill
visp rules Next betaList or remove the rules the user stated for all later work
visp trail prune Next betaRemove 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-exec with critic.harness: claude-code runs the independent reviewer and tester as restricted claude -p sessions, whichever host does the coding.
Flip check
workflow.flipCheck: auto reruns 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. Use off for 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 init adopts a whole-project check only if it passes now.
Static pages and rules
Browser journeys can open project:/index.html with no server running. visp rules keeps 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.

Reservations API44 hidden checks
44
37–40
Spreadsheet engine38 hidden checks
35–37
26–35
Bundles on an existing service60 hidden checks
60
59–60
Extending an existing engine63 hidden checks
57–63
41–59
Visp CodeOther arms
Range of hidden checks passed across runs, as a share of each task’s checks. Earlier rounds on the extension task scored 56–63 for Visp Code.
Every run, with wall time
Task (hidden checks)Visp CodeBareSpec KitBMAD
Reservations API (44)44, 44, 44 8–10 min38, 39, 40 2–4 min39, 40, 37 10–13 min38, 38, 39 3–6 min
Spreadsheet engine (38)36, 35, 37 10–16 min28, 32, 26 5–8 min28, 28, 27 12–15 min33, 32, 35 3–22 min
Bundles on an existing service (60)60, 60, 60 5–9 min60, 60, 60 2–3 min60, 59, 59 8–11 min60, 60, 60 3–5 min
Extending an existing engine (63)57, 62, 63 14–16 min51, 53, 53 9–11 min56, 59, 41 11–13 min56, 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:

ArmResolved, runs 1 and 2Of 100Mean time per task
Visp Code37, 32698.3 min
Bare32, 34663.5 min
Spec Kit30, 346410.8 min
BMAD33, 29626.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

work says 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 that critic.harness is codex and the Codex CLI is signed in. From a sandboxed shell without network, rerun visp done with sandbox escalation.
An edit or commit was refused for scope
The error names the out-of-scope files. Update the brief and run visp work again rather than widening scope by hand. A denial that blames the installation is fixed with visp doctor.
STATE_BUSY, or a mismatch after upgrading
Another Visp Code writer is active; visp doctor reports lock owners and --fix recovers abandoned transactions. After upgrading, rerun visp install and restart MCP servers and host sessions. Projects with 0.4 history need visp-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.