Whatever the work — feature dev, research, hiring pipelines, content production, ops runbooks — if it walks a state machine, this engine runs it. The workflow lives in workflow.yaml: statuses, transitions, validators, side-effects, human-approval gates. Markdown files are the database. One file per work item, plain text, version-controlled, greppable.
issue-cli enforces the workflow contract step by step. The web UI is the human window. Agents, humans, and scripts are all valid clients.
The workflow engine doesn't sit on top of Postgres or a SaaS API. The substrate is a directory of markdown files — one per work item — and a few sidecar files. That choice changes everything downstream.
.md file. The filename is the slug.status, priority, assignee. Custom keys are open columns.issues/Combat/, issues/Renderer/ — tag and namespace at once.<slug>.data.json for structured rows; <slug>.stats.json for transition stats.The trade-off is real: no JOIN, no transactions across rows, no millions-per-second throughput. In return, you get a database that survives any tool change, that diffs cleanly in a code review, that anyone with a text editor can read or write, that backs up by being git push-ed, and that an LLM can reason about with the same primitives it uses on source code.
Nine statuses configured in workflow.yaml. Each transition can carry validators, side-effects, and human-approval gates. The CLI walks the path strictly; the web UI lets a human drag-and-drop to override.
Every transition is an ordered list of actions. Validators check preconditions. require_human_approval blocks until a checkbox in the web UI is ticked. Side-effects mutate the issue file or inject prompt context for the next agent.
# workflow.yaml transitions: - from: "in design" to: "backlog" actions: - type: validate rule: section_min_length section: Design min: 200 hint: "flesh out the Design section" - type: validate rule: section_checkboxes_checked section: Design - type: require_human_approval status: backlog - type: append_section title: "Implementation" body: | - [ ] Wire happy path - [ ] Cover edge cases - [ ] Add automated tests - type: inject_prompt prompt: | The design is locked. Treat the Design section as the spec; raise a comment before deviating from it.
validate — section_min_lengthBlocks the transition unless ## Design has at least 200 non-whitespace characters. The hint is appended to the failure message so the agent knows exactly what to do next.
validate — section_checkboxes_checkedEvery [ ] under ## Design must be ticked. The library includes ~15 validators: has_pr_url, linked_issue_in_status, field_in, command_succeeds, and more.
require_human_approvalThe CLI refuses the transition until a human ticks the matching checkbox in the web UI. The CLI's failure message includes a deep link to the exact approval row.
append_sectionSide-effect: drops a checklist scaffold into the issue body so the next stage starts with a known shape. Fence-aware — quoted markdown in the body won't trigger a false "section already exists".
inject_promptAdds extra guidance for this specific transition. Combines with the destination status's baseline prompt when the next agent picks up the issue.
Four patterns from a real workflow. Drop them into workflow.yaml and they take effect on the next CLI invocation.
in progress until a human approvesAgents finish design and stop, instead of plowing into implementation off a half-baked spec.
transitions: - from: "backlog" to: "in progress" actions: - type: require_human_approval status: "in progress" # Agent runs: issue-cli start fix-heat # CLI answers: missing approval — open # /p/x/issue/fix-heat#approve-in-progress # to grant it.
done without a green test suiteTemplated arguments, sandboxed env, opt-in via top-level allow_shell: true.
allow_shell: true transitions: - from: "shipping" to: "done" actions: - type: validate rule: command_succeeds command: "gh pr view {{number}} --json state -q .state | grep -q MERGED" timeout_seconds: 15 hint: "land or close PR #{{number}}"
Statuses marked optional: true are skippable on forward transitions but reachable as a CTA when needed.
statuses: - name: "in progress" - name: "waiting-for-team-input" optional: true description: "blocked on another team" - name: "testing" transitions: - from: "in progress" to: "waiting-for-team-input" cta_label: "Block on another team" actions: - type: require_human_approval status: "waiting-for-team-input"
issue-cliissue-cli is how clients drive the workflow. Bots, humans at a terminal, CI jobs, shell scripts — same interface. The CLI presents the workflow contract as a set of subcommands and refuses to violate it.
Where the web UI lets a human bypass the rules with a drag-and-drop, the CLI enforces them. create only allows idea or in design. transition moves one status forward and rejects skips. done is only valid from documentation. There is no --force.
Failure messages double as remediation: every gate rejection ends with an issue-cli command the caller can run to fix the gate. The contract is also surfaced before the transition, via the Requires: / Will: block printed underneath every == Next == hint, so the caller knows what's coming. This works as well for an LLM agent as it does for a developer running commands by hand.
workflow.yaml schema with field types and rules.documentation.A real issue-cli process transitions dump — the contract an agent reads before touching anything:
$ issue-cli process transitions
== Transition Rules ==
→ idea Initial state — title only
idea → in design Validate issue body is not empty
Side-effect: appends ## Idea section
Side-effect: appends ## Design section
Side-effect: appends ## Acceptance Criteria section
in design → backlog Validate section Design checkboxes are checked
Validate section Acceptance Criteria has checkboxes
Must be human-approved for "backlog" in the issue viewer
Side-effect: clears assignee
backlog → in progress Must be human-approved for "in progress" in the issue viewer
Validate issue has assignee
Side-effect: appends ## Implementation section
Side-effect: appends ## Test Plan section
in progress → testing Validate section Implementation checkboxes are checked
Validate test plan is present
Side-effect: appends ## Testing section
testing → human-testing Validate test plan is present
Validate section Testing checkboxes are checked
Validate comment starts with tests:
Side-effect: appends ## Human Testing section
human-testing → documentation Must be human-approved for "documentation" in the issue viewer
Side-effect: appends ## Documentation section
documentation → shipping Validate section Documentation checkboxes are checked
Validate comment starts with docs:
Side-effect: appends ## Shipping section
shipping → done Validate section Shipping checkboxes are checked
Must be human-approved for "done" in the issue viewer
Transitions are strict — you cannot skip required statuses.
An issue starts almost empty — a title and a few lines of context. As it walks the workflow, each transition appends a new section to its body. By the time the issue reaches done, the file is a self-contained record of every decision and verification that produced the change.
This is the opposite of the ticket-system model where an issue is a static description and the work happens elsewhere — in chat, in PR comments, in a wiki, in some tracker's activity log. Here the file is the activity log. Design checklists, implementation notes, the test plan, the docs links, the shipping refs, the inline review comments — they all accrete into the same markdown file, in the same git repo, under the same diff history.
The engine drives this directly: append_section side-effects on transitions scaffold the next stage's structure with empty checkboxes. The agent fills the boxes and the prose. Validators on the next transition check that the boxes were ticked. By the time you read the file at done, it tells you what the change was, why, what was considered, what was tested, and how it shipped.
Inline comments live at the bottom of each file in an HTML comment block — invisible to grep, cat, and other markdown renderers, but addressable in the web UI per-block. Per-issue structured data (code-review findings, triage rows) lives in a sidecar JSON next to the issue file. Custom frontmatter keys render as a sidebar in the detail view.
done, annotated with the transition that produced each section# --- frontmatter ---create --- title: "Fix heat overflow on burst weapons" status: "done" system: "Combat" version: "0.4" priority: "high" labels: [bug, combat] pr: "https://github.com/me/proj/pull/812" --- # --- body intro ---create Burst weapons accumulate heat across modules and overflow the i16 register at sustained fire. Reproducible with the "long burn" scenario in tests. # --- appended on idea → in design ---idea → in design ## Idea - [x] Description with enough context for someone unfamiliar - [x] Edge cases identified (overflow, underflow, multi-module) - [x] Scope clear (what is NOT included) ## Design - [x] Switch heat accumulator to i32, gate at MAX_HEAT - [x] Per-module heat decay rate - [x] Test strategy: long-burn fixture + property test ## Acceptance Criteria - [x] No overflow under 10x sustained fire - [x] Existing scenarios pass unchanged # --- appended on backlog → in progress ---backlog → in progress ## Implementation - [x] Wire i32 accumulator in combat/heat.go - [x] Migrate save format (backwards-compatible) - [x] Cover edge cases from the Design section ## Test Plan - [x] Unit: heat_test.go — long burn, edge cases - [x] Property test: random weapon mixes, 10k iterations - [x] Manual: smoke through tutorial mission # --- appended on in progress → testing ---in progress → testing ## Testing - [x] All automated tests green on CI - [x] No regression in existing fixtures - [x] tests: 10k-iteration property run completed clean # --- appended on human-testing → documentation ---human-testing → docs ## Documentation - [x] Updated docs/Combat/heat-model.md - [x] Added migration note to CHANGELOG - [x] docs: heat-model section reviewed # --- appended on documentation → shipping ---docs → shipping ## Shipping - [x] Committed with ref #42 - [x] Pushed; PR #812 merged - [x] Tagged v0.4.1, release notes polished # --- inline comments accrete throughout --- <!-- issue-viewer-comments {"id":1,"block":2,"date":"2026-04-12","text":"check legacy save format","status":"done"} {"id":2,"block":4,"date":"2026-04-14","text":"property test seed?","status":"done"} -->
Three views from the running web UI: authoring the workflow, observing what the engine has done, and feeding lessons back into the workflow itself.
workflow.yaml.
workflow.yaml.
The repo ships with a configured demo workflow, sample issues, and docs.
Boots a sample project at localhost:8080.
git clone github.com/michal-franc/ ai-native-project-viewer cd ai-native-project-viewer make demo
Walks agents through the workflow contract.
make install issue-cli process issue-cli next --version 0.1 issue-cli start <slug>
Edit workflow.yaml. Validators, gates, side-effects, overlays — all hot-reloaded.
$EDITOR workflow.yaml issue-cli process schema issue-cli process transitions