herdr-virtualboard v0.1.0

herdr virtualboard

A kanban board for your VirtualBoard feature specs, inside Herdr — where moving a card runs vb, and dispatching one starts a VirtualBoard role agent in a visible pane.

herdr · 0.9.0 gates · passing release · v0.1.0 go 1.25 linux · macos mit
Get Started → View on GitHub
The hvb board in a terminal: five columns — BACKLOG 50, IN PROGRESS 0, BLOCKED 0, REVIEW 3, DONE 107 — each card showing its feature number, title, priority, complexity, owner and acceptance-criteria progress.
The board. Five columns, one per VirtualBoard lifecycle status, with the counts repeated in the header. Each card carries its feature number, title, priority, complexity, owner and acceptance-criteria progress — 4/7 means four of seven boxes are ticked in the spec itself. Nothing here is hvb's own state: every value is read from the markdown in the repository.

Why this exists

herdr-board gives Herdr a kanban board whose cards are prompts in a SQLite database. This plugin answers a different question: you already have a board — it is in your repository.

VirtualBoard keeps feature specs as markdown under .virtualboard/features/, with the directory as the status and vb as the tool that moves them. That layout is committed, reviewed and shared.

The one decision everything follows from

hvb owns no cards. It renders the repository and writes every change back through vb, so the index, the audit log and the lock file stay correct — and a teammate running vb move from a shell sees the same board you do.

Install

herdr plugin install virtualboard/herdr-virtualboard

Open the board:

herdr plugin action invoke open-virtualboard --plugin herdr-virtualboard

Requirements: Herdr 0.9.0 (socket protocol 22), the vb CLI, a Go toolchain for the install-time build, and a VirtualBoard workspace (vb init). Linux and macOS.

A keybinding

Not configured automatically. Add it to ~/.config/herdr/config.toml:

[[keys.command]]
key = "prefix+shift+v"
type = "shell"
command = "herdr plugin action invoke open-virtualboard --plugin herdr-virtualboard"
description = "open the VirtualBoard kanban (overlay)"

With Herdr's default ctrl+b prefix that is Ctrl+B Shift+V. Do not use prefix+v — lowercase single letters are where Herdr keeps its own bindings. Then herdr server reload-config.

The board

KeyActionKeyAction
←/→ h/lfocus column↑/↓ k/jfocus card
⏎card detailesc qback, then quit
nnew featuremmove (lifecycle-checked)
H Lshift one columneset priority
ddispatch an agentofocus the running agent's pane
xcancel the active runrrefresh
?help

The board reloads every few seconds, so a teammate's vb move or another agent finishing shows up without you asking. Below about 108 columns it switches to a single-column layout with a lifecycle breadcrumb, so it stays usable in a narrow split or on a phone.

The lifecycle

VirtualBoard's transition graph is not a suggestion:

backlog ──→ in-progress ──→ review ──→ done ↑ ↓ │ blocked ──────────┘

Nothing leaves done. hvb greys out impossible moves in the picker rather than offering them and failing — but vb remains the authority, and every move still goes through it. The duplicate table exists to make the lifecycle visible, not to enforce it.

Starting work

Moving a card into in-progress — with m, or L — asks one question.

The move picker overlaying the board: Move FTR-0043, with in-progress selectable and blocked, review and done greyed out, each annotated with its reason such as 'no backlog to review transition'.
The move picker. Illegal destinations are shown and greyed out, each with the reason — no backlog → review transition — because an absent option looks like a bug, while a disabled one teaches the lifecycle. The selection opens on something choosable.

Once the move lands, a second question follows:

┌──────────────────────────────────────────────────────────────┐
│ FTR-0043 is now in progress                                  │
├──────────────────────────────────────────────────────────────┤
│ Retry failed uploads with exponential backoff                │
│                                                              │
│  ▸ No — I will work on it       just move the card           │
│    Agent, in the project        works in the project tree    │
│    Agent, in a worktree         isolated checkout + branch   │
│    Agent, worktree + PR         pushes and opens a PR        │
└──────────────────────────────────────────────────────────────┘
Why a question and not a setting

That is the moment work starts, and the only moment the person moving the card has the context to answer — they know whether this is a five-minute edit or a day's work. A config key would be answered once and then be wrong for most cards. Declining is the default and one keystroke away.

Worktrees and pull requests

A worktree run gets its own checkout on feature/FTR-0043/retry-failed-uploads-with-exponential-backoff, cut from the default branch and opened by Herdr as a linked workspace beside the project — so the agent edits a different directory from the one you are looking at, and the sidebar shows both. The card carries a branch marker (⑂) while it runs.

With + PR, a successful run pushes that branch and opens a pull request against the base, with the feature's acceptance criteria and the agent's commits in the description, then writes the link back into the spec's Links section.

hvb run start FTR-0043 --worktree --pr
hvb run cleanup FTR-0043            # remove the checkout when you are done

Forges

ForgeHowNeeds
GitHubthe gh CLInothing — it uses the credentials you already have
Forgejo, GiteaREST APIa token in forge.token or $HVB_FORGE_TOKEN
GitLab, anything else—nothing; you get a compare URL
Failure is not failure

Nothing in the pull-request path can fail a run. The agent has already done the work and committed it; losing that because a token was missing would be absurd. No token, no client for the forge, no commits, a dirty checkout, a rejected token — each is a recorded reason plus a compare URL, with the run still succeeded and the feature still moved.

The first dispatch into a new worktree

A worktree is a directory the harness has never seen, so Claude Code stops at its trust prompt. Herdr reports that as agent_not_ready, which sounds like a failure and is not: the agent is running. hvb parks the task, tells you to answer the dialog, and submits it by itself once the agent is ready. It never answers that dialog for you — whether to trust a directory is your decision.

The CLI

hvb feature list --status review          # what needs checking
hvb feature new "Add retry to the uploader" -l backend --priority P1
hvb feature move FTR-0007 in-progress     # vb enforces the lifecycle
hvb run start FTR-0007 --role backend_dev --focus
hvb run list                              # what is running now
hvb run log FTR-0007                      # what that agent is saying
hvb doctor                                # when something is wrong, start here
NounVerbs
hvb featurelist, show, new, move, set, note, delete, validate
hvb runstart, list, show, done, comment, cancel, focus, log, cleanup
hvb rolelist, show
hvb harnesslist
hvb tui · hvb doctor · hvb skill · hvb version

--json works on everything. Errors go to stderr in a stable envelope with stdout left empty, so a script can parse stdout unconditionally.

The agent contract

hvb skill prints the exact bytes embedded in every dispatch prompt, so an agent can read what it is held to rather than take hvb's word for it. The short version:

Two writers moving one spec is how a board and a repository drift apart, which is why the agent reports and hvb moves.

Configuration

Optional. ~/.config/herdr-virtualboard/config.toml for defaults, .hvb.toml beside .virtualboard/ for per-project overrides, merged field by field.

harness          = "claude"        # any kind `hvb harness list` shows
role             = "fullstack_dev" # fallback when labels suggest nothing
lock_ttl_minutes = 60              # 0 disables locking

[columns.in-progress]
auto       = false                 # dispatch on arrival
on_success = "review"
on_failure = "blocked"
timeout    = "45m"

[worktree]
enabled = false
branch  = "feature/{id}/{slug}"    # VirtualBoard's own convention

[forge]
enabled = false
draft   = true
token   = ""                       # Forgejo/Gitea; GitHub uses gh's own auth

A route the lifecycle forbids is rejected when the file loads, not when a dispatch fails hours later. The project file lives beside .virtualboard/ rather than inside it, because vb init --update re-applies the upstream template over that directory.

Exit codes

The first five mirror vb's own, so a script driving both branches on one set of numbers.

CodeMeaning
0success
1validation failed
2feature or run not found
3illegal lifecycle transition
4unmet dependency
5lock conflict
6Herdr unavailable or unsupported
64hvb refused the invocation itself — usage, a bad enum, a missing $HVB_RUN_ID

Troubleshooting

Start here, always:

hvb doctor
SymptomWhat it means
“No VirtualBoard workspace here”The board opens on your focused pane's directory. Focus a pane inside a VirtualBoard project, or run vb init.
The agent sits at a dialogIts startup trust prompt. Answer it; hvb submits the task by itself.
A run shows awaitingThe agent stopped without reporting — a closed pane, or a finished turn with no hvb run done. The feature is deliberately not moved.
No pull requesthvb run show gives the reason and a compare URL. The run still succeeded.
Exit 6Herdr is not running, or is not exactly 0.9.0 / protocol 22.

The VirtualBoard family

This plugin is one piece of VirtualBoard:

virtualboard.devthe project — markdown-first feature specs for AI–human teams
vb-clithe CLI this plugin drives every change through
template-basethe workspace template, including the agent charters hvb dispatches
herdr-virtualboardthis plugin — the board and the agent dispatcher, inside Herdr

Going deeper

The contributor-facing documentation lives in the repository as markdown:

docs/design.mdwhy there is no daemon, and what hvb does own
docs/worktrees.mdworktree dispatch, pull requests, and testing both locally
docs/herdr.mdthe verified Herdr contract, and how to re-verify it
docs/configuration.mdevery configuration key and environment variable
docs/testing.mdthe gates and the end-to-end catalogue
AGENTS.mdthe rules for agents working on this repository