VibeVM
Contents
On this page
en
Publisher
org.vibevm.core
Version
1.0.0latest
Audiences
author
Reading time
4 min
Rendered
Read aloud
never

Write a flow package

01A flow tells an agent how a team works: how to commit, what to check before pushing, how to keep notes between sessions. This page writes one from scratch, including the short text the agent reads at every session start.

02
Create a flow package org.acme/review-notes in the current VibeVM project, as an in-tree package under vibevm/vibepacks/. It should teach an agent to leave a short REVIEW.md note at the root of the project after every change it makes, with the date and what changed. Write the boot snippet, the protocol document and the manifest, then run vibe check on the package.

the vibevm skill installed for your agent; a project with vibe.toml at the root

vibevm/vibepacks/org.acme/review-notes/v0.1.0/vibe.toml declares a flow package with a boot snippet; the snippet is under two hundred words and names the protocol by address; the protocol document has anchored sections; vibe check --path vibevm/vibepacks/org.acme/review-notes/v0.1.0 reports no errors

  • vibe check --path vibevm/vibepacks/org.acme/review-notes/v0.1.0 --quiet
  • test -f vibevm/vibepacks/org.acme/review-notes/v0.1.0/vibevm/vibespecs/boot/review-notes.xml

What happens

03The agent runs vibe init package org.acme/review-notes, which adds a package slot to the project at vibevm/vibepacks/org.acme/review-notes/v0.1.0/: a manifest with a [package] table, a README and a placeholder snippet. The project's own registry sees the slot, so the flow can be installed and tried in place before it is published. It then writes three things. The boot snippet is a short instruction the agent of a consuming project reads at every session start. The protocol is the full set of rules under vibevm/vibespecs/flows/review-notes/, with anchored sections the snippet cites. The manifest's [boot_snippet] table points at the snippet with the category flow. Finally it runs vibe check on the package, which validates the manifest and the layout like it would for any project.

04 Decision. A package has the identical on-disk shape as a consumer project:

By hand

051. Create the package slot:

06
vibe init package org.acme/review-notes
Creating package `org.acme/review-notes` in `<TMP>/work`
  ✓ created  vibevm/vibepacks/org.acme/review-notes/v0.1.0/vibe.toml
  ✓ created  vibevm/vibepacks/org.acme/review-notes/v0.1.0/vibevm/vibespecs/boot/10-tool-review-notes.md
  ✓ created  vibevm/vibepacks/org.acme/review-notes/v0.1.0/README.md
  • kept     vibevm/vibespecs/boot/INDEX.md (regenerated)
  • kept     CLAUDE.md (regenerated)
  • kept     AGENTS.md (regenerated)
  • kept     GEMINI.md (regenerated)

Done. Project `org.acme/review-notes`: 3 files created, 4 kept.

Next:
  • edit vibevm/vibespecs/boot/00-core.md and vibevm/vibespecs/common as your project takes shape
  • install packages with `vibe install <kind>:<name>` (e.g. flow:wal)

072. Edit the manifest the scaffold wrote: set kind = "flow", fill in the description, and point [boot_snippet] at your snippet. As generated:

08
cat vibevm/vibepacks/org.acme/review-notes/v0.1.0/vibe.toml
[package]
group = "org.acme"
name = "review-notes"
kind = "tool"
version = "0.1.0"
epoch = 1
authors = ["vibevm docs fixtures"]
license = "UPL-1.0"
description = ""
format = "normal"

[boot_snippet]
source = "vibevm/vibespecs/boot/10-tool-review-notes.md"
category = "tool"
link = "dynamic"

093. Inside the slot, write the snippet at vibevm/vibespecs/boot/review-notes.xml. Keep it short: what to do, and the address of the rule that says why. It is read by every agent, in every session, of every project that installs your flow.

104. Write the protocol under vibevm/vibespecs/flows/review-notes/: one anchored section per rule, one idea per section, a status marker on each.

115. Check and publish:

12
vibe check --path vibevm/vibepacks/org.acme/review-notes/v0.1.0
vibe check: clean — every check passed against `<TMP>/work/vibevm/vibepacks/org.acme/review-notes/v0.1.0`

The snippet is the expensive part

13A snippet is paid for on every session start by every consumer. Write it as an instruction, not an explanation: name the rule, the address, the command. Put the reasoning in the protocol, where an agent reads it once when it needs to. And never assume another package is installed: a snippet that says «as the WAL protocol requires» in a project without that protocol sends the agent looking for a rule that is not there. If your flow builds on another, declare the concept it relies on and let vibe warn when the concept is absent.

14 Everything is layered by mutation frequency — the whole loaded context is one monotone gradient (owner, 2026-08-23, near-verbatim: «всё в приложении устроено слоями; на самой вершине — динамическая загрузка быстрых изменений»). Reading order equals stability order: the rarest-changing text reads first, because a change at depth N re-prices every byte after it — the earlier a layer sits, the more cache its mutation burns. The concrete gradient: (0) the instruction files (CLAUDE.md / AGENTS.md / GEMINI.md) — read first, so an edit there resets the ENTIRE cache; they carry only what must hit every session (the four rules, the standing directives) and change only for large causes, everything else living in specs loaded later; (1) the generated STATIC lane — structural-events-only (§3), and INTERNALLY sorted by the same law: contributions of rarer-changing packages belong earlier in the tape; (2) the INDEX manifest and the conditional dynamic lane — per-boot variability; (3) the live session tail — task text, tool results, fast state, which lives in context and is never compiled into any lane. This is a GLOBAL architectural idea for all of VibeVM, not a spec of any one mechanism (owner, 2026-08-23): whenever a new system is designed or an existing one changed, the design review checks it against this layering — where does each byte it adds sit on the gradient, and does anything fast-changing sneak ahead of anything slow.
15 A boot snippet never presupposes another discipline. A snippet speaks unconditionally only about its OWN flow; any mention of another flow's artifacts or duties is CONDITIONAL — and the only lawful conditional form is structural, not verbal: the mention lives in a snippet fragment guarded by when = "installed:<group>/<name>", so the text physically enters a project's lanes only when that discipline is actually installed. Prose hedges («if you keep a WAL», «or equivalent») are NOT the lawful form — they are unverifiable by machine and still teach the concept unasked. This is ##THE-LAYER-LAW's sibling: a presupposition must never travel ahead of its own discipline.

16The consumer decides how your snippet is linked, compiled into the priority lane or listed in INDEX.md; you may suggest a default in [boot_snippet], and the consumer's choice wins.

17 A package MAY declare a suggested default inclusion type in its own [boot_snippet]; the consumer's declaration always wins. Absent both, the type is dynamic.

Subskills: content that arrives when it is needed

18A package may split its content into subskills, the smallest units an agent can activate: each looks like a tiny package with its own manifest and files. What differs per subskill is its delivery, the primary axis: eager puts the content on disk at install, lazy-push and lazy-pull hold it back until an activation rule matches. A subskill activates when any one of its channels matches, once. For the lazy modes a description is required, because it is the whole trigger, and vibe check refuses a lazy subskill without one.

19 A subskill is the smallest activatable content unit inside a package. Structurally it looks like a tiny package: own manifest, own files, own optional further subskill children (§2.5.5). What changes per subskill is the delivery mode (§2.5.0 below) and the activation rules (§2.5.2): together they decide when the subskill's content reaches the agent and how.
20 A subskill's delivery field is the primary axis of the manifest, not a follow-up bolt-on. It picks how the subskill's content reaches the agent. Three values, each well-defined:
21 A subskill becomes "active" if any one of these channels matches. Channels compose orthogonally; an active subskill activates once regardless of how many matched. The full set, more comprehensive than revision r1:
22 description is required for delivery = "lazy-push" and lazy-pull. The activation trigger is the entire mechanism for those modes — without it, vibe-mcp has nothing to match against. eager mode also benefits but is not required. vibe check errors out (not warns) on a lazy-push subskill missing description.

What goes where

23Paths are relative to the package root, the slot vibevm/vibepacks/org.acme/review-notes/v0.1.0/.

24
Path Purpose
vibe.toml the manifest: [package], [boot_snippet], optional [[skill]], [requires]
README.md what the flow is, shown on the registry and the site
vibevm/vibespecs/boot/<name>.xml the boot snippet
vibevm/vibespecs/flows/<name>/ the protocol and its supporting documents
vibevm/vibespecs/skills/<skill>/SKILL.md skills the flow offers to agents, declared in [[skill]]
LICENSE.md the licence; permissive licences only in the public registry
25 Prompt/spec content lives under the package's spec/ subtree — boot snippets (vibevm/vibespecs/boot/), cards, guides, manifesto, appendix — laid out exactly as an ordinary project's spec/ (VIBEVM-SPEC.md §4.2). [boot_snippet].source is a spec/-relative path (e.g. spec/boot/20-stack-rust-ai-native.md).

26Everything under the package root except build output is the package: that is what is fingerprinted, copied into consumers and shown by the site.

27 Decision. A package's shippable tree is its directory minus a build-output denylist:

28A package is simple by default: its files are carried whole and read because they are present. format = "normal" opts into a split between contract/, small and cheap to load like a header, and source/, the heavy body pulled only when a directive asks for it; a normal package that nobody uses does not enter the build at all.

29 format = "simple"the default (absent format, a package is simple). Legacy / adapted prompts, carried whole, with no VibeVM-specific structure — for importing existing corpora without rewriting them, and the fail-safe posture. Rules: inclusion in [requires.packages] means (a) structural — the agent reads the file; (b) static — its text is compiled into the target. If [boot_snippet].source names a file, only that file is read/spliced; absent even that, every file in the package is read/spliced by a recursive walk — the over-load is the author's problem, the deliberate cost of not adopting normal.
30 format = "normal" — the VibeVM-native form, opt-in: the contract / source split (§4), directives (§7), and the compiler (§8). A normal package is not read just because it is present — it participates only when something actually #uses it (§7.2). This is tree-shaking; the optimized posture for authors who understand the machinery, at the price of structuring the package correctly.
31 contract/ — small, simple, boot-snippet-like. The surface a package exposes outward; short files, cheap to load. The analogue of a header.
32 source/ — large, heavy. The full implementation; pulled only when actually needed. The analogue of a translation unit.
33 Tree-shaking default. A format = "normal" package that nobody #uses does not participate — not read, not used, anywhere. The moment any text does #use spec://…, that package enters the build and MUST be linked before its user in topological order.

Edge cases and rules

34Cross-references inside the package are addresses, spec://org.acme/review-notes/flows/review-notes/PROTOCOL#anchor, never relative file paths; they survive being copied into any consumer.

35 L1 — physics moves, addresses do not. spec:// addresses, anchors, fact ids, specmap edges, scope! citations and recorded verdicts are LOGICAL and do not change: only the physical prefix maps differently (vibevm/vibespecs/common/PROP-000.xml still canonicalises to the same common/PROP-000 document path). Any relayout step that would rename an address is a defect of the step.
36 The layout: every project and every package carries ONE distinctive root directory vibevm/, holding vibevm/vibespecs (was spec/), vibevm/vibepacks (was packages/), vibevm/vibedeps (was root vibedeps/) and vibevm/vibefacts (was root vibefacts/). Nothing else moves; vibe.toml stays at the project root.

37While you develop a package inside the same repository, edit its source and run vibe install: the change reaches the dependency tree without a flag, an update or a forced reinstall.

38 No new flag (consistent with §2.5). This is automatic and source-aware — the author edits the in-repo source and runs vibe install; nothing to remember, and neither vibe update nor reinstall --force is needed for the local-authoring loop, while those bypasses remain for the immutable case.

39Versions never move: to change a published snippet, bump the version and publish again. A consumer sees the change at its next update, not before.

40A flow may require other packages; a consumer installing yours gets them too, and their snippets enter the consumer's lane after yours.

For an agent

This page has a machine mirror. The citation carries the version rather than latest, so what an agent quotes does not move under it.

spec://org.vibevm.core/vibevm-docs@1.0.0/authoring/write-a-flow

.md.xmlllms.txt