draiver

command module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 2 Imported by: 0

README

Draiver

Agents are cattle, and it takes a village of agents to raise a ticket.

A Continuous Development tool that is a coordination substrate for supervising AI coding agents at the ticket level. It replaces the issue tracker's human-to-human status protocol with a human-to-agent one, on a single premise: agents are cattle, tickets are pets. You manage tickets, not agents. A fresh agent can pick up any ticket from disk, and a human attends only to the tickets that need them.

Issue Tracking software manage the interface between managers and developers. This aims to manage the interface between developers and agents.

Agents work well with CLIs (they have HATEOAS built in) and humans work well with UIs so this also has a Htmx web ui built in.

Purpose

Externalize the valuable context — spec, decisions, escalations — so no agent's in-flight context is ever precious. One human supervises N tickets by attending only to escalations and reviews, not progress. Management by exception, at scale.

The current "development cycle" that is emerging is frontloaded and extensive design session with AI coding tools, that produce comprehensive designs as artifacts, either in an Issue tracker ticket or in a markdown file somewhere. Then you "babysit" AI agents, often in parallel on different tickets, until you have working PRs. The design at the front and the PR review at the back are the dev job, and the babysitting is new toil: managing context, loading tickets into agents, asking "Do you have everything you need", then jumping to the currently blocked AI agent to help advance it.

This tool is one thing leading to another:

  1. Durable state/context, associated with the ticket, so that agents can be "stateless" (obviously they're intensely stateful, but let the context be an input)
  2. Agent management comes naturally from the durable state, with swimlanes per ticket.

Attempts outlive the agent

An attempt is the progression of a ticket toward a PR — running brief to load context, logging gotchas and decisions, escalating when blocked, applying resolutions once answered. One attempt can span many escalate→resolve cycles; escalation pauses the work, it doesn't abandon the ticket. What it need not span is a single agent: the session that starts an attempt is rarely the one that finishes it.

The point of draiver is that the attempt is not trapped inside the agent's context window. When a session fills its context, drifts, or derails, you don't lose the work. Kill the agent — it's cattle. Start a fresh session; it runs brief, replays the spec and the log, and resumes exactly where the last one left off: same decisions, same resolved escalations, no re-litigation. The log is the memory; the agent is disposable. That resumption is the product.

Escalations are asynchronous. The agent runs escalate, which records the question durably and returns a nonzero exit, and then its turn ends — it does not sit and poll for an answer. The human isn't watching the CLI; they watch the board (draiver webui), where the ticket surfaces under Needs me, and from there they resolve it — via the CLI today, in-UI once manage mode lands. Resolving is the human's move, not the agent's. The next attempt — resumed or brand-new — re-enters through brief and reads the resolution inline. A blocked ticket and its answer are one durable artefact, independent of whichever agent picks it up next.

Storage: append-only, portable per ticket

The ticket is the atomic, portable unit — team, project, and assignee are fields, not folders, so tickets move freely between them. Markdown files contain frontmatter. A folder per ticket; the shared spec.md sits at the top, and the mutable state lives under one folder per attempt.

PROJ-123/
  spec.md                 # frontloaded design — the immutable input, SHARED by every attempt
  attempts/
    0001/                 # one attempt = one journey from a starting point (its own working tree)
      attempt.md          # provenance: tool, model, actor, started (+ reserved room for metrics)
      log/                # append-only events, one write-once file each; its own hash chain
      artefacts/          # blobs the log references, never inlines
      state.md            # generated projection — do not edit
    0002/                 # a separate attempt: different tool/model, for comparison and audit
      attempt.md  log/  artefacts/  state.md

An attempt owns one log and one hash chain, and spans many respawned sessions (§ Attempts outlive the agent). Separate attempts are the comparison unit: run claude-code against aider, or re-try a stuck ticket, and each keeps its own durable trail against the same spec.md. Event type is a field, not a directory; structure lives in references between events, never in the tree. Corrections are new events; history is never rewritten.

Vocabulary (CLI verbs = the shared protocol)

Discoverable via --help; the verbs are to agents what the status enum is to Issue Tracker.

  • new PROJ-123 --title … — create a ticket (spec.md) and its first attempt. A title is mandatory and comes from exactly one source: --title, or a --spec import file whose frontmatter already carries a title:. --spec f.md --title T titles a title-less import T (injecting the frontmatter); supplying a title from both sources, or from neither, is rejected and writes nothing. Blank/whitespace titles count as unset.
  • brief PROJ-123 — replay spec + log into a context blob that cold-starts a fresh agent. If brief isn't enough to resume, the design is leaking state.
  • escalate — append an escalation event and halt (nonzero exit). The gate is enforced by process control, not agent goodwill.
  • resolve — the human's answer, appended and linked back. Escalation + resolution is one durable artefact.
  • inbox --mine — unresolved escalations across all attempts of all tickets.
  • status — regenerate the projection(s).
  • audit PROJ-123 - verifies the hash-chained log of every attempt (see below)
  • attempt new PROJ-123 --tool … / attempt ls PROJ-123 — start or list attempts. Verbs act on the ticket's latest attempt by default; --attempt 0002 (or DRAIVER_ATTEMPT) targets a specific one.

Control states (not progress states)

The dashboard columns are the human's relationship to the ticket, weighted asymmetrically. Control state is per attempt, so a ticket with two live attempts shows as two cards:

  • Running — agent working, no action. Rendered as a count.
  • Needs me — unresolved escalation. This is the board.
  • Review — agent claims done; a claim, not a fact. Load-bearing. Reversible: logging a decision against a Review attempt reopens it to Running (a first-class Review→Running transition), the decision text recording why — no escalate/resolve workaround.
  • Done.

Auditability: hash-chained log

Each event carries the hash of its predecessor, so any edit breaks the chain — tamper-evidence without git, and without git's container problem (which conflicted with mobile tickets). Attribution (actor, ts) lives in the event data, not in git blame.

Persistence and speed

  • Filesystem - default, e.g. ~/.draiver/data/
  • S3 — Easy sync. Completed tickets can be archived with zip → upload → drop from the working set. Versioning gives a second, independent tamper-evidence layer.
  • SQLite — local, rebuildable cache/index over the canonical log. Powers fast agent queries and the board. Never the source of truth; the backend must never surface its schema in the verbs.

Dashboard

UIs are for humans, so draiver webui will run a Htmx webserver (dynamic SPA, self contained within the cli tool). It can be used in a Read Only fashion, by just pointing at a data folder, or can be used in a Manage mode, being able to spin up agents, refresh agent context (kill agent and reload a fresh one with the same context), or restart ticket with a separate attempt if stuck (still append only, creates a new, potentially concurrent attempt on the ticket, possibly even with different inference model or coding agent). Renders the four control states. Joins to the Issue Tracker by ticket ID: the thin glanceable state syncs up for managers, the rich log stays down for agents and devs.

Agent control: JSON-stream over stdio

When webui is run in Manage mode:

Agents are driven headless, not screen-scraped. The backend spawns one agent per ticket in an isolated worktree and speaks newline-delimited JSON both ways (--input-format/--output-format stream-json), relayed to the browser over WebSocket/SSE. Backend-agnostic via thin per-agent adapters (Claude Code, Aider, Codex, …).

Two hooks map straight onto the model: the session ID is the cattle mechanism — kill the process freely, keep the ID plus the log, respawn and --resume. The tool-permission callback is the escalation seam — route "may I?" to escalate instead of auto-approving, and the agent's own authority boundary becomes the human-in-the-loop gate.

Onboarding skill: install as a Claude Code plugin

The agent side of the protocol — cold-start from brief, log gotchas/decisions, escalate and stop, claim review — is packaged as a Claude Code skill, shipped through a plugin marketplace rooted at ./.claude. Layout:

.claude/
  .claude-plugin/
    marketplace.json          # marketplace "draiver" → one plugin
  plugins/
    draiver/
      .claude-plugin/
        plugin.json           # plugin "draiver" v0.1.0
      skills/
        draiver-onboarding/
          SKILL.md            # auto-discovered; the working-a-ticket protocol

Install it into Claude Code by adding this repo as a marketplace, then installing the plugin:

/plugin marketplace add ./.claude       # or a GitHub repo, once published: /plugin marketplace add <owner>/<repo>
/plugin install draiver@draiver         # <plugin>@<marketplace>

The draiver-onboarding skill then fires whenever an agent is handed a ticket. The same flow works headless via the CLI: claude plugin marketplace add ./.claude then claude plugin install draiver@draiver (claude plugin details draiver shows the discovered components).

License

MIT © 2026 David Wilcox

Documentation

Overview

Command draiver is a coordination substrate for supervising AI coding agents at the ticket level: an append-only, hash-chained per-ticket log plus a read-only board. Agents are cattle, tickets are pets.

Directories

Path Synopsis
Package cmd wires the draiver CLI verbs — the shared human<->agent protocol — onto the append-only per-attempt ticket log.
Package cmd wires the draiver CLI verbs — the shared human<->agent protocol — onto the append-only per-attempt ticket log.
internal
agent
Package agent is the thin seam between draiverctld and a coding agent.
Package agent is the thin seam between draiverctld and a coding agent.
agent/claudecode
Package claudecode implements the agent.Adapter seam over Claude Code driven headless — one `claude` process per session, spoken to in newline-delimited stream-json on stdio (`--input-format stream-json --output-format stream-json`).
Package claudecode implements the agent.Adapter seam over Claude Code driven headless — one `claude` process per session, spoken to in newline-delimited stream-json on stdio (`--input-format stream-json --output-format stream-json`).
attempt
Package attempt creates and reads attempts on a ticket.
Package attempt creates and reads attempts on a ticket.
audit
Package audit verifies an attempt's hash-chained log: each event must recompute to its stored hash and link to its predecessor.
Package audit verifies an attempt's hash-chained log: each event must recompute to its stored hash and link to its predecessor.
brief
Package brief replays a ticket's spec and one attempt's log into a single context blob that cold-starts a fresh agent.
Package brief replays a ticket's spec and one attempt's log into a single context blob that cold-starts a fresh agent.
config
Package config loads draiverctld's operator configuration — the tunables the supervisor reads but the ticket data does not own.
Package config loads draiverctld's operator configuration — the tunables the supervisor reads but the ticket data does not own.
event
Package event defines the atomic, write-once unit of a ticket's log: a hash-chained markdown file with YAML frontmatter.
Package event defines the atomic, write-once unit of a ticket's log: a hash-chained markdown file with YAML frontmatter.
gate
Package gate is the supervisor's "Gate" step (reconcile loop step 3) and the second of the two hooks: the permission gate.
Package gate is the supervisor's "Gate" step (reconcile loop step 3) and the second of the two hooks: the permission gate.
limit
Package limit is the supervisor's context-window backstop: the third enforcement seam alongside the protocol gate and the permission gate.
Package limit is the supervisor's context-window backstop: the third enforcement seam alongside the protocol gate and the permission gate.
manage
Package manage binds one attempt's coding-agent adapter, its git worktree, and its persisted session identity into a single live-session controller — the "agents are cattle" mechanic in one type.
Package manage binds one attempt's coding-agent adapter, its git worktree, and its persisted session identity into a single live-session controller — the "agents are cattle" mechanic in one type.
project
Package project derives an attempt's control state — the human's relationship to it — from its append-only log, and renders the generated state.md projection.
Package project derives an attempt's control state — the human's relationship to it — from its append-only log, and renders the generated state.md projection.
protocol
Package protocol is the supervisor's protocol gate (reconcile loop step 3): it enforces the draiver protocol by process control rather than agent goodwill.
Package protocol is the supervisor's protocol gate (reconcile loop step 3): it enforces the draiver protocol by process control rather than agent goodwill.
reconcile
Package reconcile is draiverctld's reconcile loop — the supervisor's active half, the piece that ties drvctl-001–007 together into a running daemon.
Package reconcile is draiverctld's reconcile loop — the supervisor's active half, the piece that ties drvctl-001–007 together into a running daemon.
session
Package session persists a draiverctld session's runtime state: the session/ directory under an attempt, holding session.json (identity), meter.json (token/cost/context + watchdog counters), and stream.jsonl (the raw stream-json tee).
Package session persists a draiverctld session's runtime state: the session/ directory under an attempt, holding session.json (identity), meter.json (token/cost/context + watchdog counters), and stream.jsonl (the raw stream-json tee).
store
Package store resolves the on-disk layout of the ticket data root.
Package store resolves the on-disk layout of the ticket data root.
ticketlog
Package ticketlog reads and appends an attempt's write-once, hash-chained event log.
Package ticketlog reads and appends an attempt's write-once, hash-chained event log.
watch
Package watch is the supervisor's "Watch" step: it consumes one session's normalized event stream and does the two jobs the reconcile loop's step 2 names — promote and meter.
Package watch is the supervisor's "Watch" step: it consumes one session's normalized event stream and does the two jobs the reconcile loop's step 2 names — promote and meter.
web
Package web serves the read-only Draiver board: a self-contained HTMX server that renders the four control states per ATTEMPT from the filesystem log.
Package web serves the read-only Draiver board: a self-contained HTMX server that renders the four control states per ATTEMPT from the filesystem log.
worktree
Package worktree manages the per-session git worktree that isolates one draiverctld session, so parallel sessions on the same repo never collide and competing attempts stay comparable.
Package worktree manages the per-session git worktree that isolates one draiverctld session, so parallel sessions on the same repo never collide and competing attempts stay comparable.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL