#+title: Getting started #+description: Everything below runs in a scratch directory and touches nothing else. #+options: toc:nil num:nil Everything below runs in a scratch directory and touches nothing else. By the end you will have a tracker two people, or two scripts, can work at once without colliding. * 1. Make a tracker Creating the first issue creates the file. #+begin_src console $ mkdir -p /tmp/demo && cd /tmp/demo $ vissue create --project parser "Reject a manifest with no header" parser-k29f TODO [#C] Reject a manifest with no header file: /tmp/demo/Software/parser/issues.org #+end_src That file is ordinary Org: #+begin_src org #+TITLE: parser issues #+VISSUE: 1 #+CATEGORY: parser #+FILETAGS: :issues:parser:noexport: #+TAGS: { bug(b) feature(f) task(t) chore(c) plan(p) } #+TAGS: docs(d) perf ignore ARCHIVE #+PRIORITIES: A C C #+EXCLUDE_TAGS: noexport #+SELECT_TAGS: export #+DATE: [2026-08-03 Mon] #+DESCRIPTION: Issue tracking file for parser specs, plans, and implementation tasks. #+STATUS: Active #+TODO: TODO STARTED BLOCKED | DONE CANCELLED * TODO [#C] Reject a manifest with no header :PROPERTIES: :ID: parser-k29f :CREATED: [2026-08-03 Mon] :END: #+end_src Nothing about it is private to this tool. Open it in Emacs and the agenda, tag search, and =id:= links all work. An agent must not =Write= or =StrReplace= that file: =vissue= and the MCP server are the writers, and two creates on the same file serialize under a lock. A hand edit does not join that lock. * 2. Add work that waits on other work #+begin_src console $ vissue create --project parser --priority A \ --body "Scope: the error message quoted in the release notes." \ "Publish the release notes" parser-3xq7 TODO [#A] Publish the release notes $ vissue update parser-3xq7 --block parser-k29f parser-3xq7: state TODO -> BLOCKED (auto on block), blocked_by += parser-k29f #+end_src Adding a blocker moved the issue to BLOCKED on its own, and the transition is in the logbook. An edge that would close a cycle is refused. * 3. Ask what is actually workable #+begin_src console $ vissue ready parser-k29f TODO [#C] Reject a manifest with no header #+end_src The blocked issue is gone from the list, which is the whole point of =ready=. It is the open frontier, not a schedule. A parent with =:ORDERED: t= holds later children out of that list until the earlier ones are =DONE= or =CANCELLED=. * 4. Work an issue, name what it made, and close it #+begin_src console $ vissue claim parser-k29f claimed parser-k29f by you@yourhost (TODO -> STARTED) $ vissue deed parser-k29f --add deed-patch-manifest-header parser-k29f: deeds += deed-patch-manifest-header parser-k29f: 1 deed deed-patch-manifest-header $ vissue update parser-k29f --state DONE parser-k29f: state STARTED -> DONE, claim released (you@yourhost) [hint] parser-3xq7 (in parser) lists this as a blocker; clear with `vissue update parser-3xq7 --unblock parser-k29f` #+end_src Closing a blocker gives up the claim and names every issue still waiting on it. The hint goes to standard error, so a pipeline reading standard output is unaffected. The accession in the middle is the handoff. [[https://leidarljos.github.io/deedar/][deedar]] mints one for the thing the work produced and keeps the bytes; the tracker keeps the id, so whoever picks up the next issue can open the product instead of asking what happened. Nothing here requires deedar to be installed: the tracker only ever stores the id. * 5. Watch the backlog open up, and hand the product on #+begin_src console $ vissue update parser-3xq7 --unblock parser-k29f parser-3xq7: state BLOCKED -> TODO (auto on unblock), blocked_by -= parser-k29f $ vissue ready parser-3xq7 TODO [#A] Publish the release notes #+end_src Unblocking dropped the edge, so =recall= now has nothing to hand over. Ask it before clearing the blocker instead, which is the ordinary order: the next worker claims, reads what its inputs produced, and only then starts. #+begin_src console $ vissue recall parser-3xq7 parser-3xq7 BLOCKED Publish the release notes (parser) Inputs parser-k29f DONE Reject a manifest with no header [blocked-by] deed-patch-manifest-header Produced (nothing cited yet) Body (no body) #+end_src * 6. Put a second worker on it This is what the graph is for. A claim records who holds an issue, so the second worker is told there is nothing free rather than repeating the first one's work. The identity is an opaque string: a person, a machine, a script. #+begin_src console $ VISSUE_AGENT=impl-1 vissue claim parser-3xq7 claimed parser-3xq7 by impl-1 (TODO -> STARTED) $ VISSUE_AGENT=impl-2 vissue ready # empty: the only open work is claimed #+end_src The second worker sees nothing to take. =claims= is the standing answer to who holds what: #+begin_src console $ vissue claims parser-3xq7 STARTED [#A] 0d impl-1 Publish the release notes (parser) #+end_src A claim held by another identity is refused unless you pass =--force=, which records the takeover in the logbook rather than losing it. * 7. Compare-and-swap, then watch the log Two writers do not last-write-wins. Name the generation you read, and a later write refuses if the corpus moved: #+begin_src console $ vissue gen 7 $ vissue update parser-3xq7 --state DONE --if-gen 7 #+end_src =gen=, =events --since=, and =wait= are the log a second process polls. The tracker is those files and that log. Completing a session elsewhere does not close an issue. * 8. Share it with someone who cannot reach the tracker #+begin_src console $ vissue mirror --project parser --out /tmp/demo/backlog-mirror.org wrote /tmp/demo/backlog-mirror.org #+end_src That file is a read-only projection with a banner saying so, and a stamp that makes staleness one command: #+begin_src console $ vissue mirror --check /tmp/demo/backlog-mirror.org fresh: digest=d2ee07c7f585330b issues=2 generation=7 (stamped 2026-08-03T09:53) #+end_src It exits 0 when current and 1 when not, so a script can gate on it. * Where next - [[file:howto.org][How-to]] for the rest of the verbs, one task at a time. - [[file:emacs.org][Emacs]] to put this tracker in the Org agenda, where its deadlines and tags already work. - [[file:explanation.org][Explanation]] for why the order is a partial order and why nothing infers an edge you did not write.