Venture · a primer for product owners

Charters, Plans, and Beats

How a charter is written, how plans carry the work, and how the beat derives the heartbeat everyone reads. Nothing here is typed into a status report.

A project is a charter. The record is a git repository. Everything else is derived.

The shape

Three kinds of repository

Venture is not a tracker with a database. The system is a small constellation of ordinary git repositories, each with one job. Nothing syncs, because there is nothing to sync: one store, two kinds of client.

Work repos · one per codebase

where developers build

Ordinary DevArch repos, linked to the record once. Plans, sessions, and code live here.

.devarch/descriptor.json
docs/work/<slug>/plan.md
docs/context/.project-outbox.jsonl

outbox events

latest beat, at session start

The project record

the store

A docs-only repo. Humans commit opinions above the line; the generator derives everything below it.

project.md · criteria/ · scenarios/
.devarch/project.json + hooks
─────────────
events.jsonl · heartbeat.md · beats/

reads your clone

commits in your name

Venture · the face

what the product owner sees

Ships inside DevArch. devarch project serve renders the record in a local browser, and every edit made there lands as a gated git commit. It derives nothing and stores nothing.

board · heartbeat · criteria · scenarios
serves on 127.0.0.1 only

Work repos sit beside the record as sibling clones. The generator reads their outboxes across every ref; what is committed anywhere counts, not just what is merged.

The record

The charter, and the two gates

A record exists because a product owner wrote a charter against an objective: a theory that reaching a stated end state will move an outcome that lives outside engineering. The record holds the charter's terms; whether the theory was right is measured afterwards, against the objective. A project that reaches its end state while the outcome doesn't move is a wrong theory, not a failed project, which is what makes abandoning mid-flight inexpensive and respectable.

Creation happens through two gates, and each gate is a list of fields someone must put on the table:

GateEnforced byWhat must exist
Proposedevarch project init refuses to scaffold without its argumentsa slug, a product owner, an objective file. A project without a charter is just a scenario list.
Activatethe record's own pre-commit hook blocks any commit setting status: Activean end state (one paragraph of prose), at least one criterion, a boundary (a date and/or a session count: the value proposition), exclusions (none is an answer, a placeholder is not), a named PM (may equal the PO), and at least one context in project.json.

The Activate gate is a tracked file inside the record (.devarch/hooks/pre-commit, enabled per clone with git config core.hooksPath .devarch/hooks). The gate travels with the record, not with the tool; a clone that has never seen DevArch is still gated, which is exactly what lets Venture write through it.

What's in the record

PathAuthorWhat it is
project.mdPO / PMthe charter's fields (status, boundary, exclusions, roles) and the end state prose
criteria/C-nnn-<slug>.mdPOone file per acceptance criterion; criteria/examples/ shows each kind and never counts
scenarios/S-nnn-<slug>.mdPM / devsfrontmatter + Given/When/Then are yours; the ## Phases table below is generator-maintained
.devarch/project.jsonPMcontexts (which repos, who owns them), thresholds, beat cadence
events.jsonl · heartbeat.md · beats/generator onlyderived on every beat. A hand edit here isn't forbidden; it surfaces as a Record defect on the next beat

The rule that holds it together

Three writers, three verbs

Every file in the system has exactly one kind of author, and each kind writes in exactly one way. This is what makes the record forensic rather than aspirational: nothing in it is typed after the fact.

Humans commit opinions

Criteria, scenarios, assignments, the boundary, exclusions: in the record, as ordinary git commits. Through an editor or through Venture; the same hooks gate both.

Agents append events

Never to the record. A DevArch session appends one line per phase change to an outbox in its own work repo, committed with the work. Nobody types these.

The generator derives the rest

devarch project beat is the sole deriver. Events, heartbeat, scenario tables, stamped beats: recomputed from scratch every run. Derived files are never inputs.

For developers

Plans carry the work

The honest answer to "what do developers do differently?" is: almost nothing. You plan and build the way DevArch already has you plan and build. Two small things change, and both are enforced by hooks rather than memory.

  1. 1

    Link the repo, once

    devarch project link venture ../venture-project writes a project key into .devarch/descriptor.json. Commit it; every clone now points at the same record.

  2. 2

    Every plan phase names its scenario

    A - **Scenario**: S-nnnline beside the phase's Status. The scenario-gate hook blocks a phase from going CURRENT without one; a phase serving no scenario is work outside the project, and it stays PENDING until someone says which charter it serves.

  3. 3

    Sessions open on the beat

    Session start prints [Project beat: …], the latest committed heartbeat. You read the project's actual state before your first edit, instead of reconstructing it from memory or chat.

  4. 4

    Work. The outbox writes itself

    When a phase flips status in plan.md (started, done, blocked), the project-outbox hook appends one JSON line to docs/context/.project-outbox.jsonl. You type nothing. The file is committed with the work, never edited by hand.

  5. 5

    A beat runs in the record

    On its cadence, on notable events, or whenever someone commits to the record, the record's own post-commit hook runs devarch project beat --commit. It merges every work repo's outboxes across every ref, derives the state, and lands one derivation commit carrying a Derived-By trailer.

  6. 6

    Everyone reads the same heartbeat

    PO, PM, and devs look at the same derived heartbeat.md, in the record or rendered by Venture. devarch project beat --for <dev>prints one developer's filtered view and writes nothing.

Nobody runs a standup for the record.Status is not reported; it is derived from what actually happened: phase events committed with the work, commands read from the record's own git history. If the heartbeat is wrong, something real is wrong, and it shows up as a defect rather than a stale board.

The generator

What a beat actually does

One command, run inside the record (or by its post-commit hook). It reads two things and nothing else: the record's own hand-authored files and git history, and the outboxes and plans of the sibling work-repo clones, across every ref plus the working tree, so an unmerged branch's progress still counts.

From those inputs it derives, from scratch, every run:

  • events.jsonl: the merged event stream, outbox events from the work repos, plus command events (ProjectActivated, ScenarioAssigned, CriterionAdded…) derived from the record's commits. Each record commit is a command; the log is its receipt.
  • heartbeat.md: the one-page state of the charter (anatomy below).
  • each scenario's ## Phases table: which plan phases, in which repos, serve it, and where they stand.
  • beats/<stamp>.md: a frozen copy, stamped on the cadence and on notable events. The beats directory is the project's flip-book.

Defects are reported, never refused.The beat always renders. A hand-edited derived file, an outbox line whose author doesn't match its committer, a write into another developer's outbox, a context repo missing from disk: each becomes one line in Record defects, naming the file and the commit. The record cannot be quietly corrupted; it can only be visibly argued with.

Anatomy of a heartbeat

# Heartbeat — Venture — 2026-08-30

## Record defects
None.

## Pulse
| Measure  | Used  | Bound        | Share |
| Sessions | 0     | —            | —     |
| Calendar | day 1 | 124 days     | 1%    |
| Criteria | 0     | 1            | 0%    |

## Criteria
| C-001 | capability | unverified | S-002 |

## Scenarios
| S-001 | david.cornelson | Assigned  |
| S-002 | —               | Unassigned |

## Unassigned · ## Blocked · ## Open decisions

## Since last beat
- CriterionAdded C-001 (david.cornelson)
- ProjectActivated (david.cornelson)
- ScenarioAssigned S-001 → david.cornelson
Record defectsIntegrity findings, named precisely. The section everyone should read first, and the one that stays honest because it can't be edited away.
PulseThe value proposition burning down. Marks fire at 70 / 90 / 100% of the boundary, so the escape-hatch conversation has a scheduled trigger, not a vibe.
Scenarios & WIPWho owns what, what's unassigned, what's blocked and for how long, against the thresholds the PM set in project.json.
Since last beatThe delta, event by event, each attributed to the human whose commit produced it.

Venture itself

The face over the record

Venture presents the familiar surfaces of a project tool. Underneath, every view is the record and every action is a commit:

The product owner seesWhat it actually is
A board: scenarios by state, owner, WIPa read of the generator's derived state; the face never derives
Heartbeat, criteria list, scenario viewsreads of heartbeat.md, criteria/, scenarios/, fresh from the files on every request
Edit the charter; author a criteriona git commit to the record in your name, a form instead of an editor
A trackera front door onto a record that cannot be quietly corrupted

The write path is worth being precise about. devarch project serve runs the face locally against your own clone of the record, on 127.0.0.1 and nowhere else. Venture holds no credentials and no state. A write validates the form, writes the file, and commits in your clone with the record's own hooks running, authored with your git identity. A change the Activate gate refuses comes back to the form carrying the gate's own words. A product owner clicking Activate meets exactly the same gate a developer meets in a terminal.

Why this matters: after a hard quarter, everyone wants to know who decided what, when, against which criteria. Versioned criteria, attributed events, and stamped beats are a better forensic trail than any board.

One charter, end to end

The sequence

Every mechanism above, on one timeline. Solid blue arrows are humans committing opinions through the gates; solid grey arrows are agents appending events with the work; dashed arrows are derived state being read. Nothing here is typed after the fact.

Product ownerEDITOR · OR VENTUREProject managerCUTS · ASSIGNS · LINKSThe recordSTORE · HOOKS · GENERATORWork repoDEV · DEVARCH SESSIONdevarch project init: the charter, proposedPropose gate: nothing scaffolds without a slug, a PO, an objectivecommit · status: Active▮ pre-commit gate: end state · criterion · boundary · exclusions · PM · contextscenarios cut · contexts linkedS-nnn-<slug>.md · project.jsondevarch project linkdescriptor.project, committed once[Project beat: …]session start reads the latest committed beatplan phase names its scenario▮ scenario-gate: no S-nnn, no CURRENTphase flips → outbox event appendedone JSON line, committed with the workoutboxes & plans read, across every refunmerged branches still countdevarch project beatderives events.jsonl · heartbeat.mdscenario tables · a stamped beateveryone reads the same heartbeatin the record, or rendered by the Venture facedevarch project close done|abandonedthe retrospective asks two questions; there is no third

The gates (▮) are tracked files inside the record, so the same checks meet a terminal, an editor, and the Venture face. The beat usually runs from the record's own post-commit hook; deriving is triggered by commits, not by anyone remembering to run it.

Endgame

Done, Abandoned, and the two questions

Done is reachable by the team alone (every criterion verified) and never waits on the market. Abandoned is first-class and inexpensive: the theory looks wrong, or the value proposition is spent, and the product owner writes the next charter. Either way, devarch project close done|abandoned writes a retrospective with exactly two fixed questions:

  • Was the end state reached: which criteria verified, which dropped, and why? (Answerable from the record alone.)
  • Was the theory right, or is that not yet knowable? (About the theory; not yet knowable is a complete answer.)

There is no third question. Lessons-learned essays are editorial; the record keeps facts.

Reference

Command crib

shell
# write the charter (Propose gate = the argument list)
devarch project init <slug> --po <person> --objective <path> [--name N] [--book B]

# point a work repo at its record (once, then commit the descriptor)
devarch project link <slug> <path-or-remote>

# serve the face locally (stop with --stop)
devarch project serve

# derive. usually the record's post-commit hook runs this for you
devarch project beat [--commit] [--stamp|--no-stamp] [--for <dev>] [--repos <dir>]

# close the charter
devarch project close done|abandoned

In a fresh clone of the record, enable its gates once: git config core.hooksPath .devarch/hooks. The beat requires jq; the face requires python3 (checked by devarch doctor).