VisExtension Center

Drafts

A draft is an isolated working copy of the current repository. The session works inside it. draft_approve() commits the work, fast-forwards the default branch, restores local work and pushes to origin when configured.

Only the agent manages drafts. There is no draft slash command, picker or menu in the TUI or Companion app.

Sandbox tools

Tool Effect
draft_create("name") Open a draft from committed HEAD and move the session into it. clean=False explicitly copies pending trunk changes; those may overlap on approval. One draft at a time.
draft_status() Report the draft branch, target_branch, draft commits the target lacks (ahead) and pending paths (pending).
draft_approve() Commit pending work, fast-forward the default branch and publish to origin if configured. draft_approve("subject") sets the subject. The draft stays open.
draft_discard() Return to the original checkout and remove the draft working copy. Approved work stays on the default branch; unapproved changes are lost. A merged draft branch may be removed.

Inside the turn that opens or discards a draft, sandbox confinement changes at once. session["workspace"] and project_root_path follow from the next block. While in a draft, session["workspace"]["draft"] includes label, backend, branch, target_branch, approved_ahead and pending_paths.

Approval

The target is the local branch named by origin/HEAD. Without that symbolic ref, Vis selects main, then master. If the target does not exist locally, approval refuses rather than selecting an arbitrary branch. Vis never switches the branch in the original checkout.

Approval fetches the target from origin when configured. The draft must contain both the local target and that fetched commit. Otherwise approval refuses with :draft/sync-required: merge or rebase onto the reported commit in the draft, resolve conflicts, then retry. Approval itself never merges target history. An existing merge, rebase, cherry-pick or revert must be finished or aborted.

Pending draft work is committed on vis/<name>, excluding backend bookkeeping. The subject defaults to draft(<name>): approve; commits include Vis-Session and Vis-Draft trailers. Local landing is fast-forward only. A linked target checkout is updated in place; an unchecked-out branch is updated by compare-and-swap.

The target checkout can be dirty. Overlapping paths are conservatively refused before any stash or landing, even when edits affect different lines in one file. Unrelated staged, unstaged and untracked work is saved in an approval-owned stash, then restored with --index. Existing user stashes are preserved. Ignored files are not stashed and cannot be overwritten by landing.

Push to origin happens only after successful restoration, without force. With no origin, approval is local only. The result includes published, branch, target_branch, commit and files. nothing-to-approve means the draft and local target already match with no pending changes; it still retries publication.

If restoration fails, no push is attempted and the saved stash is retained. Recover and verify local work manually, then drop that approval stash. Further approval is blocked while an approval stash remains. If push fails, the error explicitly reports that landing succeeded locally. Local work is already restored; fetch, synchronize the draft if necessary and retry. A rejected push does not roll back local history. Remote movement after fetch is rejected by normal Git push. Failed commits and extension vetoes are failures, not successful approvals.

Backends

Backend How the draft is made Where it lands
worktree git worktree add from HEAD; pending work is excluded by default Fast-forward the shared target, restore local work, then push to origin if configured.
rift Copy-on-write clone, reset to committed HEAD by default Fetch the draft branch into the original repository, then use the same approval flow.

worktree needs a git repository with at least one commit; rift works in any directory with clean=False but needs the Rift native library. Clean drafts require Git history. The draft_backend toggle chooses between them:

Value Meaning
auto (default) worktree when the repository allows it, else rift.
worktree, rift Only that backend; draft_create refuses when it is unavailable.
off No drafts. draft_create explains why.

Set it from the Settings dialog or in ~/.vis/config.yml:

toggles:
  draft_backend: worktree

Draft working copies live under ~/.vis/drafts/. Additional roots follow their own draft policy from the filesystem_roots configuration (see Configuration).

Hooks for extensions

Every create, approve and discard goes through the draft/create, draft/approve and draft/discard operations, so a Python extension can guard or observe them with vis.OpHook. A before hook returning vis.block(reason) stops the operation. Each approval-created draft commit also crosses git/commit; Git's own hooks are not bypassed. See Extension API.

See also

  • Configuration โ€” the draft_backend toggle and the draft policy of extra roots.
  • Extending Vis โ€” op hooks on draft/create, draft/approve and draft/discard.