Workspaces
A workspace is a root repository with other repositories mounted in it as submodules, declared beside the repos: list in repos.yaml.
Entries are named by id:
workspaces:
product:
root: product
root_pointer_default: ignore # or bump
members:
api: { repository: api, path: services/api }
| Key | Meaning |
|---|---|
workspaces.<name> | The workspace's name. |
root | The id of the root repository's repos: entry. |
root_pointer_default | ignore (default) or bump: what happens to the root's submodule pointers after members merge. See Publication. |
members.<name>.repository | The id of the member's repos: entry. |
members.<name>.path | Where the member mounts in the root. |
Connecting a repo that has submodules declares its workspace for you, each
submodule a member. A submodule you already connected on its own, as a clone
elsewhere with the same origin, is that member, settings and all; Kraft adds
an entry for the submodule's own checkout only when it finds none. When you file a work item against the root, you choose which
members it changes and its root-pointer policy (the workspace's 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, and a task with scope: each_repository runs once per selected repository.
Kraft checks each member out as a worktree of its connected repository, the
same way it checks out the root. So 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. A member whose repository isn't
connected any more is checked out with git submodule update instead.
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) or 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:.
Each selected repository binds the tasks that run in it with its own policy layer. A task in the assembled worktree, which holds all of them at once, runs under the tightest of their 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 (sandbox or policy.sandbox), or one that comes
from a chain, node or task, wraps the item's whole checkout. The container sees
each member as it sees the root: the member's refs go through a
ref store of its own, and
the files git trusts for it are read-only. Before Kraft runs git in a member, it
checks that the member is still the checkout Kraft made from its connected
repository. If it isn't, the item stops for a person, naming the member. A
member whose repository isn't connected stops a sandboxed item too: connect it,
then retry.
A sandboxed worker on a plain repository can still create a git repository of
its own inside its worktree, and commit a gitlink to it. That nested
repository's config belongs to the worker. Kraft's own git therefore never
works inside a nested repository. Its status and diff calls compare only the
commit a gitlink records, and its automatic commit of leftover work skips
nested repositories. The daemon pins submodule.recurse,
fetch.recurseSubmodules, push.recurseSubmodules, diff.submodule,
status.submoduleSummary and diff.ignoreSubmodules, whatever your own git
config says. If a sandboxed item's worktree holds a nested repository Kraft did
not create, the item stops for a person, and the stop names the paths. That
includes an untracked one, a populated gitlink, or a gitlink the branch added
or moved. Remove them, or git rm --cached the gitlinks, and retry. A
submodule your repository already had, left unpopulated, doesn't stop anything.
While one of a sandboxed item's sessions is still running, Kraft runs no git in its worktree at all. When one of those sessions ends, Kraft may move the item's branch in your repository to the commit the worker left, through the ref store, without reading the worktree. The diff view answers that the diff is available once the sandboxed session ends. A task that needs the review package stops for a person, and the sweep of leftover work waits for the last task of the step.
Under a sandbox, the clean check before a merge request no longer looks for uncommitted edits inside a submodule. It still catches a submodule whose commit moved, and each declared workspace member is checked on its own. Leftover work is no longer committed into a submodule's pointer unless that submodule is one of the item's declared members.
Publication
Publication goes members first. A member's merge request merges before the root
moves. A root with source changes of its own gets its own merge request, and 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: ignore leaves it alone, and bump pushes the new pointers
to its default branch. If that push is refused, it opens a merge request for them
instead. A member that fails to merge stops publication and leaves the root
untouched.
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.