# Workstreams

> The main ← preprod ← dev chain, promote and inherit, ownership and inherit settings, protected workstreams, removing and restoring.
>
> Source: https://www.weave.directory/docs/workstreams

A **workstream** is a line of work: `main`, `preprod`, `dev`, `feature/auth`. Everyone on a workstream shares its **head**. Every workstream except `main` has a **parent**, and together they form a chain. Weaved stitches move **up** the chain by promotion and **down** by inheritance.

```
main        weaved = deployable to production
 └─ preprod weaved = deployable to staging
     └─ dev weaved = the team's integrated work
```

## Names

A workstream name is one or more parts separated by `/`: `main`, `feature/auth`, `dev/section-of-system`. Parts use letters, digits, `-`, `_` and `.`; no empty, `.` or `..` parts; at most 100 characters. `weave workstream new` lower-cases the name and turns spaces into `-`. In a URL path a name is one segment, so encode `/` as `%2F` (`/workstreams/feature%2Fauth/head`).

## Create a workstream

```bash
weave workstream new feature/auth --from dev            # creates it and switches to it
weave workstream new sandbox --from main --ownership workstream --inherit manual
weave workstream list                                   # parent, head, ownership, inherit, weaved count
weave checkout dev                                      # switch the files on disk to another workstream
```

`--from` (alias `--base`) defaults to the active workstream. Over the server API:

```bash
curl -s https://api.weave.directory/api/weave/$REPO_ID/workstreams \
  -H "Authorization: Bearer $WEAVE_TOKEN" -H "Content-Type: application/json" \
  -d '{"slug": "feature/auth", "parent": "dev", "settings": {"ownership": "repo", "inherit": "auto"}}'
```

```json
{ "type": "workstream", "workstream": {
    "slug": "feature/auth", "parent": "dev", "parent_head": "…", "synced_parent_head": "…", "head": "…",
    "settings": { "ownership": "repo", "inherit": "auto" },
    "created_by": "<userId>", "created_at": "2026-10-30T12:00:00Z", "archived_at": null, "archived_by": null } }
```

An existing name answers `409 conflict`. A removed (archived) workstream keeps its name: restore it instead. In the web UI: the repository's **Workstreams** tab → **New workstream**, pick **Created from**, click **Create**.

## Parents can't change later

There is no call to change a workstream's parent. **Create the chain in order, top down.**

### Set up main ← preprod ← dev

1. Create the repository with only `preprod` (so it doesn't create the default `dev` under `main`):

   ```bash
   curl -s https://weave.directory/api/v1/orgs/acme/repos \
     -H "Authorization: Bearer $WEAVE_PAT" -H "Content-Type: application/json" \
     -d '{"name": "shop", "workstreams": ["preprod"]}'
   ```

   This gives `main` and `preprod` (parent `main`).

2. Create `dev` under `preprod`, in a checkout or over the API:

   ```bash
   weave workstream new dev --from preprod
   # or
   curl -s https://api.weave.directory/api/weave/$REPO_ID/workstreams \
     -H "Authorization: Bearer $WEAVE_TOKEN" -H "Content-Type: application/json" \
     -d '{"slug": "dev", "parent": "preprod"}'
   ```

3. Check: `weave workstream list` (or `GET /api/weave/{repoId}/workstreams`) should show `dev → preprod → main`.

Seeding from a template? Seed `main` first, then create `preprod` and `dev`, so they start from the seeded head (see [For AI agents](https://www.weave.directory/docs/agents#seed-a-new-app-from-a-template)).

## Promote: move a stitch up

```bash
weave timeline                         # weaved stitches of the active workstream, with ids
weave promote 9c1e --to preprod        # a stitch id prefix
weave promote 9c1e --to preprod --with-deps
weave status                           # per stitch: promotes cleanly / needs <stitch> / clashes on <symbol>
```

- Promotion submits the stitch to the target's own weave check and keeps its author, agent, intent and why.
- If it relies on earlier stitches that aren't in the target yet, Weave lists them and asks; `--with-deps` promotes them together. With `--json` and without `--with-deps` you get the error code `needs_dependencies` with the list.
- A symbol that changed differently in the target opens a **change request** there instead of weaving.
- Between two workstreams with `ownership: repo`, ownership is not checked again.

API: `POST /api/weave/{repoId}/workstreams/{from}/promote` with `{"to": "preprod", "changesets": ["<id>"], "with_dependencies": false}`. In the web UI, the **Weaved** tab has **Promote to {parent}** on each stitch.

## Inherit: receive the parent's weaves

When the parent weaves a stitch, each child receives it according to its `inherit` setting:

| `inherit` | What happens |
| --- | --- |
| `auto` (default) | The child receives the stitch at once, for every symbol it hasn't changed itself. |
| `manual` | Nothing until you run `weave refresh` (or the **Inherit from parent** button), which inherits each pending parent stitch in order. |

Symbols the child changed differently get a **change request** as soon as the clash exists — you know early, not at promotion time.

## Change requests between workstreams

A change request shows the base version and both sides, each with author, agent, intent and why. Nobody merges lines.

```bash
weave cr list                          # open change requests in this workstream (--all for resolved)
weave cr show <id>                     # both sides
weave integrate <id|workstream>        # private integration draft: clean symbols applied, clashes listed
weave cr resolve <id> this             # keep this workstream's version
weave cr resolve <id> other            # take the incoming version
weave cr resolve <id> edit             # take the symbol as edited on disk (or --text / --from-file)
weave stitch -i "Take dev's token refresh"   # build, test, then weave the resolution
```

The integration draft is private until you stitch it, so nobody sees a half-resolved state.

## Settings: ownership and inherit

| Setting | Values | Meaning |
| --- | --- | --- |
| `ownership` | `repo` (default) | One owner per symbol across every `repo` workstream. Approval happens once; promotion between such workstreams never re-asks. |
| | `workstream` | The workstream has its own owners (an experimental sandbox). Promoting from it can raise change requests for the target's owners. |
| `inherit` | `auto` (default) / `manual` | See above. |

Change them later:

```bash
curl -s -X PATCH https://api.weave.directory/api/weave/$REPO_ID/workstreams/dev/settings \
  -H "Authorization: Bearer $WEAVE_TOKEN" -H "Content-Type: application/json" \
  -d '{"inherit": "manual"}'
```

Only a repository admin, or the creator of a non-`main` workstream (with write access), may change settings. Changing `ownership` while someone else holds a live claim in the workstream's scope is refused with `409`, unless an admin sends `"force": true`. In the web UI: the workstream's page → **Settings** → **Save settings**.

## Protected workstreams

A protected workstream only changes by **promotion** (and inheritance from its parent). Direct stitches, web edits, restores, accepted suggestions, change-request resolutions and reverts are refused for everyone but repository admins (`403 "workstream main is protected: changes arrive by promotion only"`).

You can also require **attestations** — signed statements such as `tests`, `security` or `approval` — on the source head before a promotion is let in:

```bash
# repo admin
curl -s -X PUT https://api.weave.directory/api/weave/$REPO_ID/workstreams/main/protection \
  -H "Authorization: Bearer $WEAVE_TOKEN" -H "Content-Type: application/json" \
  -d '{"requireAttestations": ["tests", "approval"]}'

# preflight: is preprod's head ready to promote into main?
curl -s "https://api.weave.directory/api/weave/$REPO_ID/workstreams/main/protection/check?head=preprod" \
  -H "Authorization: Bearer $WEAVE_TOKEN"
```

A promotion without them answers `409 attestations_required` with `required`, `missing` and `invalid`. Admins are not exempt: to release without attestations an admin removes the protection (`DELETE …/protection`), which is logged. How to sign an attestation: [Decisions & history](https://www.weave.directory/docs/decisions-and-history#attestations).

## Remove and restore a workstream

Removing a workstream **archives** it: it is hidden from lists, read-only, and its history and head stay readable by name (`weave cat --workstream`, `weave timeline`). Nothing is deleted.

```bash
weave workstream remove feature/auth            # alias: archive
weave workstream remove feature/auth --force    # even with weaved stitches not promoted to the parent
weave workstream list --archived                # removed ones too, "(removed <date> by <user>)"
weave workstream restore feature/auth
```

- Refused for `main` (`400`), for the active workstream (switch first), while it has child workstreams that aren't removed (`409`, listed), and while weaved stitches aren't promoted to its parent unless `--force` (`409 unpromoted_stitches`, with the count).
- Only its creator or a repository admin may remove or restore it. Path-scoped and verifier agents never may.
- On removal, open change requests are closed (keeping its own side), pending suggestions are rejected, claims in its own ownership scope are released and open resolution points are closed. Restoring does not reopen them; `weave refresh` catches it up with its parent.
- `weave undo` of a removal restores it. A restore is refused while the parent is removed.

API: platform `DELETE /api/v1/repos/{repoId}/workstreams/{ws}?force=true|false` and `POST /api/v1/repos/{repoId}/workstreams/{ws}/restore` (personal tokens and worker agent tokens with `repo:write`), or the server routes `DELETE /api/weave/{repoId}/workstreams/{ws}?force=` and `POST …/restore`. Web UI: **Workstreams** → **Remove workstream** (tick **Remove it anyway.** when asked), and **Show archived** → **Restore**.

## Hibernate: park your draft

```bash
weave hibernate          # park your draft in this workstream; files on disk show the weaved head
weave checkout main      # look at something else
weave checkout feature/auth && weave resume   # bring the draft back, carried onto the current head
```

`weave checkout` refuses while files on disk have edits you haven't added.
