# Set up a workspace {#root}

@status:doc/work @audience:user,author

[p01] 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.

[p02]
```prompt
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

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

- assert: `test -f packages/notes-flow/vibe.toml`
- assert: `test -f packages/notes-docs/vibe.toml`
- assert: `test ! -e packages/notes-flow/vibe.lock`
- assert: `vibe check --quiet`

## What happens {#what-happens}

[p03] 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.

> [p04] Membership is **explicit** — there is no auto-discovery of directories that happen to carry a `vibe.toml`. The structure is declared, per the owner's "the whole structure is in the project description" requirement.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#EXPLICIT-MEMBERSHIP>

[p05] 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.

> [p06] **Decision.** The development tree is **one** source tree (one git repository, or not in git at all if the project is private). Workspace members are subdirectories; the split into packages is logical, at the vibevm resolver level. **Publishing is a separate operation that copies the content of a package's directory into a new, separate repository** in the registry org and tags the version — exactly what `vibe registry publish` does today for one package, repeated per self-published member by `vibe workspace publish`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#ONE-SOURCE-TREE>

> [p07] **Decision.** One `vibe.lock`, at the absolute root of the workspace tree (§2.3). No per-member lockfiles.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#ONE-LOCKFILE>

> [p08] **Command bubbling.** A command (`vibe install`, `vibe build`) run inside a member's directory walks up to the absolute root, finds `vibe.lock`, and operates against it. The member "does not notice" it is part of something larger — this realises the owner's requirement that a developer can work inside a sub-project unaware of the surrounding workspace.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#COMMAND-BUBBLING>

## By hand {#by-hand}

[p09] 1. In the root `vibe.toml`, declare the members; globs are allowed:

[p10]
```sh
cat vibe.toml
```

```output
[project]
name = "work"
version = "0.0.1"
authors = ["vibevm docs fixtures"]

[workspace]
members = ["packages/*"]
```

[p11] 2. Give each member its own `vibe.toml` with a `[package]` table, the same fields `vibe init package` writes, with the kind you mean:

[p12]
```sh
cat packages/notes-flow/vibe.toml
```

```output
[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"
```

[p13] 3. Install from the root, or from anywhere inside the workspace; the command finds the root and resolves the whole tree:

[p14]
```sh
vibe install --assume-yes
```

```output
nothing declared — regenerating boot artifacts for the empty world
vibe install: vibe.lock unchanged — nothing to re-resolve (3 nodes up to date)
```

## One manifest, three roles {#one-manifest}

[p15] 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.

> [p16] `[package]` and `[project]` are **mutually exclusive** in one file — a node is either a publishable package or a plain project, not both. (Decision 7-α from the design session: keep the two sections distinct rather than folding `[project]` into a `[package]` with optional `kind`. Explicitness wins; `kind` stays strictly mandatory wherever `[package]` appears.)
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PACKAGE-XOR-PROJECT>

## Members referring to each other {#members-referring}

[p17] 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.

> [p18] Each member is a directory carrying its own `vibe.toml` (§2.2).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#MEMBER-IS-NODE>

[p19] 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.

> [p20] **Decision.** A third dependency source-kind joins registry-resolved (PROP-002 §2.2) and git-source (PROP-002 §2.4.1): **path-source**.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PATH-SOURCE>

> [p21] **Dual-form.** `path` is used during local development inside the workspace; `version` takes effect when the consuming node is itself published — the published copy references `org.vibevm.world/wal@^0.1` from a registry, not `../flow-wal` (which an external consumer does not have). This is cargo's `{ path = ..., version = ... }` shape. Dual-form is **required** for any path-dep whose consumer is publishable.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#DUAL-FORM>

> [p22] **Decision.** Named version placeholders, the equivalent of Maven `<properties>`:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#VERSION-PLACEHOLDERS>

[p23] 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.

> [p24] **Decision.** Each publishable node declares its publish posture in `[package]`:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PUBLISH-POSTURE>

> [p25] `vibe workspace publish [--member <m>]` walks members in **topological order** (dependency-first) and skips `publish = false`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PUBLISH-TOPOLOGICAL>

> [p26] Publish is **not atomic**: on the first failure the command stops and reports what was already published and what remains. (Distributed publishing across N independent host repos has no transaction; a rollback would be a worse lie than a clear partial-progress report.)
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#PUBLISH-NOT-ATOMIC>

## Edge cases and rules {#edge-cases}

[p27] 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.

> [p28] Nesting is **hierarchical grouping**, not independent resolution domains. The lockfile and unified resolution always live at the *absolute root* of the workspace tree. A nested `[workspace]` provides (a) the `[workspace.versions]` matryoshka (§2.6) and (b) logical grouping of members — never its own lockfile, never its own resolution pass.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#NESTING-PRINCIPLE>

[p29] `vibe install -p <member>` narrows what is reported, not what is resolved: the lock file and the dependency tree are always workspace-wide.

> [p30] `-p <member>` scopes resolution *reporting* to one member; the materialisation and the single root lockfile are always workspace-wide — unified resolution admits no per-member subset.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#SCOPE-FLAG>

[p31] The dependency tree lives once, at the root; members do not get their own copies of shared packages.

> [p32] Materialised dependencies — a `vibedeps/` tree at the **absolute workspace root** (PROP-007 §2.3), written only by `vibe`. One slot per resolved package, `vibedeps/<group>.<name>/<version>/` (identity-keyed, PROP-022 §2.1 — owner ruling 2026-08-13), holding the package's published tree verbatim ([PROP-024 §2.2](../../common/PROP-024-code-bearing-packages.xml#shippable-tree) re-scopes "published" to the **shippable tree** — source minus build output — for code-bearing packages). A package's prompt content lives under its own `spec/`, so a boot snippet materialises at `vibedeps/<slot>/spec/boot/<file>` (PROP-024 §2.1). Unified resolution (PROP-007 §2.4) guarantees one version per package, so one slot serves the whole workspace.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#TREE-VIBEDEPS>

