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.
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 --quiettest -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:
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:
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:
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 isdynamic.
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:
22descriptionis required fordelivery = "lazy-push"andlazy-pull. The activation trigger is the entire mechanism for those modes — without it,vibe-mcphas nothing to match against.eagermode also benefits but is not required.vibe checkerrors out (not warns) on a lazy-push subskill missingdescription.
What goes where
23Paths are relative to the package root, the slot vibevm/vibepacks/org.acme/review-notes/v0.1.0/.
| 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'sspec/subtree — boot snippets (vibevm/vibespecs/boot/), cards, guides, manifesto, appendix — laid out exactly as an ordinary project'sspec/(VIBEVM-SPEC.md§4.2).[boot_snippet].sourceis aspec/-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.
29format = "simple"— the default (absentformat, a package issimple). 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].sourcenames 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 adoptingnormal.
30format = "normal"— the VibeVM-native form, opt-in: thecontract/sourcesplit (§4), directives (§7), and the compiler (§8). Anormalpackage 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. Aformat = "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.xmlstill canonicalises to the samecommon/PROP-000document 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 directoryvibevm/, holdingvibevm/vibespecs(wasspec/),vibevm/vibepacks(waspackages/),vibevm/vibedeps(was rootvibedeps/) andvibevm/vibefacts(was rootvibefacts/). Nothing else moves;vibe.tomlstays 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 runsvibe install; nothing to remember, and neithervibe updatenorreinstall --forceis 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.