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:
| Gate | Enforced by | What must exist |
|---|---|---|
| Propose | devarch project init refuses to scaffold without its arguments | a slug, a product owner, an objective file. A project without a charter is just a scenario list. |
| Activate | the record's own pre-commit hook blocks any commit setting status: Active | an 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
| Path | Author | What it is |
|---|---|---|
| project.md | PO / PM | the charter's fields (status, boundary, exclusions, roles) and the end state prose |
| criteria/C-nnn-<slug>.md | PO | one file per acceptance criterion; criteria/examples/ shows each kind and never counts |
| scenarios/S-nnn-<slug>.md | PM / devs | frontmatter + Given/When/Then are yours; the ## Phases table below is generator-maintained |
| .devarch/project.json | PM | contexts (which repos, who owns them), thresholds, beat cadence |
| events.jsonl · heartbeat.md · beats/ | generator only | derived 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
Link the repo, once
devarch project link venture ../venture-projectwrites aprojectkey into.devarch/descriptor.json. Commit it; every clone now points at the same record. - 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
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
Work. The outbox writes itself
When a phase flips status in
plan.md(started, done, blocked), the project-outbox hook appends one JSON line todocs/context/.project-outbox.jsonl. You type nothing. The file is committed with the work, never edited by hand. - 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 aDerived-Bytrailer. - 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.
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
## Phasestable: 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
project.json.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 sees | What it actually is |
|---|---|
| A board: scenarios by state, owner, WIP | a read of the generator's derived state; the face never derives |
| Heartbeat, criteria list, scenario views | reads of heartbeat.md, criteria/, scenarios/, fresh from the files on every request |
| Edit the charter; author a criterion | a git commit to the record in your name, a form instead of an editor |
| A tracker | a 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.
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.
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
# 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).