<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">Set up a workspace</title>
  <status stage="doc" state="work" audience="user,author"/>
  <p p="1">Several packages developed together can live in one repository and share one record of the versions they use. This page turns a folder into such a workspace and shows how members refer to each other by path.</p>
  <prompt id="set-up-a-workspace" p="2">
    Turn the VibeVM project in the current folder into a workspace with two member packages under packages/: org.acme/notes-flow and org.acme/notes-docs, where notes-docs documents notes-flow. Run an install and show me that one lock file at the root covers both members.
    <needs>the vibevm skill installed for your agent; a project with `vibe.toml` at the root</needs>
    <outcome>the root `vibe.toml` carries a `[workspace]` table listing both members, each member folder carries its own `vibe.toml` with a `[package]` table, and one `vibe.lock` at the root records the resolution</outcome>
    <assert>test -f packages/notes-flow/vibe.toml</assert>
    <assert>test -f packages/notes-docs/vibe.toml</assert>
    <assert>test ! -e packages/notes-flow/vibe.lock</assert>
    <assert>vibe check --quiet</assert>
  </prompt>
  <section id="what-happens" title="What happens">
    <p p="3">The agent adds a `[workspace]` table to the root [manifest](../glossary/index.xml#manifest) naming the member paths, and writes each member's `vibe.toml` by hand with a `[package]` table: a member is a plain package folder, and `vibe init package` scaffolds a different layout, the in-tree slot the authoring pages describe. Membership is explicit: a folder with a manifest that is not listed is not a member. Then it runs `vibe install` at the root: vibe discovers the workspace, resolves the requirements of every member in one unified resolution, and writes a single [lock file](../glossary/index.xml#lock-file) at the root.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#EXPLICIT-MEMBERSHIP" p="4"/>
    <p p="5">One source tree and one lock file at its root: members are folders, the split into packages is logical, and publishing copies a member's folder into a repository of its own. A command run inside a member walks up to the root and works against the root's lock, so a developer can work inside a sub-project without noticing the workspace around it.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#ONE-SOURCE-TREE" p="6"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#ONE-LOCKFILE" p="7"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#COMMAND-BUBBLING" p="8"/>
  </section>
  <section id="by-hand" title="By hand">
    <p p="9">1. In the root `vibe.toml`, declare the members; globs are allowed:</p>
    <example id="workspace-table" fixture="workspace-root" p="10">
      <run>cat vibe.toml</run>
      <expect>[project]
name = "work"
version = "0.0.1"
authors = ["vibevm docs fixtures"]

[workspace]
members = ["packages/*"]</expect>
    </example>
    <p p="11">2. Give each member its own `vibe.toml` with a `[package]` table, the same fields `vibe init package` writes, with the kind you mean:</p>
    <example id="member-manifest" fixture="workspace" p="12">
      <run>cat packages/notes-flow/vibe.toml</run>
      <expect>[package]
group = "org.acme"
name = "notes-flow"
kind = "flow"
version = "0.1.0"
epoch = 1
authors = ["vibevm docs fixtures"]
license = "UPL-1.0"
description = ""
format = "normal"</expect>
    </example>
    <p p="13">3. Install from the root, or from anywhere inside the workspace; the command finds the root and resolves the whole tree:</p>
    <example id="install" fixture="workspace" p="14">
      <run>vibe install --assume-yes</run>
      <expect>nothing declared — regenerating boot artifacts for the empty world
vibe install: vibe.lock unchanged — nothing to re-resolve (3 nodes up to date)</expect>
    </example>
  </section>
  <section id="one-manifest" title="One manifest, three roles">
    <p p="15">Every node has a file named `vibe.toml`, and what the file contains decides what the node is. A `[package]` table makes it a publishable package; a `[project]` table makes it a consumer that is never published; a `[workspace]` table makes it [coordinate](../glossary/index.xml#coordinate) members. A node cannot be both a package and a project, but a workspace root may be either, or neither.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PACKAGE-XOR-PROJECT" p="16"/>
  </section>
  <section id="members-referring" title="Members referring to each other">
    <p p="17">A member requires a sibling by path rather than by [registry](../glossary/index.xml#registry), with a `path` source in its requirements. The lock file records such an entry with a source kind of `path` and the member's folder relative to the root, so the lock stays portable across machines. When the workspace is published, the path becomes an ordinary coordinate in each published copy.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#MEMBER-IS-NODE" p="18"/>
    <p p="19">A path is a third source of packages beside registries and git. A member requires another by path during development and by version once published, in one line of two halves, `{ path = "../flow-wal", version = "^0.1" }`: the published copy names the registry version, which an outside consumer can resolve, and the path never leaves the workspace. A version placeholder declared once at the root stands in for a number repeated across members.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PATH-SOURCE" p="20"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#DUAL-FORM" p="21"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#VERSION-PLACEHOLDERS" p="22"/>
    <p p="23">Each publishable member declares its posture with `publish` in `[package]`. `vibe workspace publish` walks the members dependency-first, skips `publish = false`, and stops at the first failure with a report of what was published and what remains; there is no rollback, because a clear partial report is better than a pretended transaction.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PUBLISH-POSTURE" p="24"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PUBLISH-TOPOLOGICAL" p="25"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PUBLISH-NOT-ATOMIC" p="26"/>
  </section>
  <section id="edge-cases" title="Edge cases and rules">
    <p p="27">Workspaces nest: a member may itself carry a `[workspace]` table. Nesting groups members; it does not create separate resolution domains, and the one lock file stays at the absolute root.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#NESTING-PRINCIPLE" p="28"/>
    <p p="29">`vibe install -p &lt;member&gt;` narrows what is reported, not what is resolved: the lock file and the dependency tree are always workspace-wide.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#SCOPE-FLAG" p="30"/>
    <p p="31">The dependency tree lives once, at the root; members do not get their own copies of shared packages.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#TREE-VIBEDEPS" p="32"/>
  </section>
</spec>
