# sow build

> Converge Desired Membership and renderer configuration into a complete Built Generation.

---

LLMS index: [llms.txt](/llms.txt)

---

`sow build` is the explicit Desired-to-Built convergence command. It acquires the Repository write
lock, recovers any decidable unfinished Operation, renders and verifies a complete Generation, then
switches protocol pointers last.

## Synopsis

```text
sow build [-j|--jobs N] [-C|--workdir DIR] [-r|--repo NAME] [-d|--dist NAME]... [-T|--timeout DUR | -N|--no-wait] [--json]
```

| Flag | Meaning | Default |
|---|---|---|
| `-j, --jobs N` | Parallel workers; must be at least `1` | logical CPU count |
| `-C, --workdir DIR` | Workspace discovery start directory | current directory |
| `-r, --repo NAME` | Select a Repository | [selection rules](/docs/command/#repository-selection) |
| `-d, --dist NAME` | Build named Dists; repeatable | all affected Dists |
| `-T, --timeout DUR` | Maximum lock wait; `0` waits indefinitely | `0` |
| `-N, --no-wait` | Fail immediately when the lock is held | false |
| `--json` | Wrap the result in the `sow.cli/v1` envelope | false |

Without `-d`, SOW converges every affected Dist in the selected Repository. With `-d`, only those
Dists converge; unselected changes remain dirty.

## Result

Without `--json`, `build` prints one human-readable summary:

```console
sow build -r demo -d el9
built repository=demo operation=2769214987359113555 dists=el9 revision=4 generation=00000000000000000005 dirty=false
```

Use `--json` for the command-specific object inside the standard envelope.

## No-op builds

When membership, relevant policy, renderer settings, and signing configuration already match the
Built Generation, `build` is an idempotent no-op and does not increment the Generation:

```console
sow build -r demo -d el9
build repository=demo dists=el9 already current (noop) revision=4 generation=00000000000000000005 dirty=false
```

## Policy convergence

`build` re-evaluates the current `exclude` and `limit` policy. Tightening policy may remove Desired
Membership. Loosening policy does not reconstruct historical members from leftover pool bytes; run
[`sow add`](/docs/command/add/) again for packages you want to restore.

## Commit and recovery

SOW stages new metadata on the same filesystem, verifies it, then switches mutable protocol pointers
last. RPM checksum-named metadata and APT by-hash keep old and new readers self-consistent.

Pending package promotion is a bounded, single-writer group commit. Each batch contains at
most 512 objects or 1 GiB: SOW creates Pool links, persists every distinct target parent,
then removes pending names and persists the shared pending directory. An interruption can
leave a pending-only, exact dual-link, or Pool-only state, all recoverable from the journal;
it cannot durably lose both names.

One Operation may cover several Dists. Each Dist always exposes a complete view; when `build`
returns, every included Dist belongs to the same Built Generation.

Before starting new work, `build` attempts forward recovery or safe rollback of a non-terminal
Operation. If journal, database, and filesystem evidence contradict each other, the Repository enters
`error` and `build` refuses to guess. There is no force-repair flag.

## Progress events

Long builds append structured `build_progress` records to the Operation log. Each event
contains `phase`, `completed`, `total`, and `jobs`. Current phases are:

- `rendering`;
- `promoting_payload`;
- `publishing_dists`;
- `normalizing_public_tree`;
- `finalizing`.

These events do not advance the Operation state and deliberately do not checkpoint SQLite
after every update. They are audit/observability records, not recovery decisions. Inspect
them with [`sow log OPERATION`](/docs/command/log/#one-operation-in-detail).

## Metadata signing

Managed metadata signing is configured only in `sow.yml`; there is no command-line key override.
Changing a configured key reference or fingerprint makes affected Dists dirty, and the next build
re-signs their metadata.

- RPM: always writes `repodata/repomd.xml`; writes `repomd.xml.asc` when configured.
- DEB: always writes `Release`; writes `InRelease` and `Release.gpg` when configured.

## Exit codes

| Code | Trigger |
|---|---|
| `0` | Converged successfully or nothing to do |
| `1` | Renderer, signing, or filesystem failure |
| `2` | Usage error, Workspace not found, or implicit Repository selection is ambiguous |
| `4` | Repository write lock unavailable |
| `5` | Recovery cannot complete safely, or Repository is in `error` |
| `6` | Explicit scope is not configured, or current configuration rejects existing state |

## See also

- [`sow status`](/docs/command/status/) — determine whether convergence is needed
- [`sow check`](/docs/command/check/) — verify the resulting tree
- [`sow changes`](/docs/command/changes/) — inspect the resulting file delta
- [Transactions & Recovery](/docs/feature/transactions/) — full commit protocol
