nextv2.0.0
repos.yaml

Workspaces

Declare a root repository with member submodules in repos.yaml, and what it means for sandboxing and publication.

A workspace is a root repository with other repositories mounted in it as submodules, declared beside the repos: list in repos.yaml.

config/repos.yaml
repos:
  - path: /home/you/code/product
    id: product
    setup_command: "make setup"
    test_command: "make test"
  - path: /home/you/code/api
    id: api
    setup_command: "make setup"
    test_command: "make test"

workspaces:
  product:
    root: product
    root_pointer_default: ignore     # or bump
    members:
      api: { repository: api, path: services/api }

root and repository each name a repos: entry by its id.

KeyTypeDefaultMeaning
workspaces.<name>mappingThe workspace. Its name is the key.
workspaces.<name>.rootstring, a repo id(required)The root repository's repos: entry.
workspaces.<name>.root_pointer_defaultignore or bumpignoreWhat happens to the root's submodule pointers after members merge. See Publication.
workspaces.<name>.membersmapping{}The members, each keyed by its own name.
workspaces.<name>.members.<name>.repositorystring, a repo id(required)The member's repos: entry.
workspaces.<name>.members.<name>.pathstring(required)Where the member mounts in the root. See Member paths.

A repos.yaml from before workspaces still loads, but an item with submodules needs a declared workspace. Declare it in workspaces:, or reconnect the root, which loses its settings.

Connecting a workspace

Connecting a repo that has submodules declares its workspace for you, each submodule a member.

A submodule you already connected on its own (a clone elsewhere with the same origin) is that member, with its settings. Kraft adds an entry for the submodule's own checkout only when it finds none.

Filing a work item against the root

When you file a work item against the root, you choose:

  • which members it changes
  • its root-pointer policy (the workspace's root_pointer_default unless you pick one)

Both are frozen into the item. The item's worktree holds exactly those members, each on the item's branch. A task with scope: each_repository runs once per selected repository.

Member checkouts

  • Kraft checks each member out as a worktree of its connected repository, the same way it checks out the root. The item's branch exists in that repository too.
  • Abandoning or archiving the item deletes the member's worktree and branch there, as long as the member is still connected. If you disconnect or move it first, remove them by hand.
  • Archiving keeps a member's branch that has commits nowhere else.
  • A member whose repository is not connected any more is checked out with git submodule update instead.

Member paths

Every member mounts directly in the root. repos.yaml fails to load, and filing an item against that workspace is refused, when:

  • one member's path is inside another member's (libs/a/vendor/x inside libs/a)
  • two members share a path

The error names both members. A submodule nested in a member is part of that member's repository, so leave it out of members:.

Policy layers

Each selected repository applies its own policy layer to the tasks that run in it. A task running in the assembled worktree, which holds every selected repository, gets the tightest combination of those layers: allowlists intersect, deny lists add up, and numbers take their minimum.

Sandboxing

A workspace with members can be sandboxed like a single repository: a sandbox on the root or on any member, or one that comes from a chain, node or task, wraps the item's whole checkout. Each member's refs go through a ref store of its own, and a member that is not the checkout Kraft made, or not connected, stops the item.

Git in a sandbox tells how members are mounted, and what Kraft's git does there, a nested repository Kraft did not create included.

Publication

Publication goes members first: a member's merge request merges before the root moves.

  • A member that fails to merge stops publication and leaves the root untouched.
  • A root with source changes of its own gets its own merge request. It stays a draft until every member has merged and the root names their merged revisions.
  • A root whose only change is the members' pointers follows the item's root-pointer policy:
PolicyWhat happens to the root
ignoreIt is left alone.
bumpKraft pushes the new pointers to its default branch. If that push is refused, it opens a merge request for them instead.
Copyright © 2026