# Build tasks

> How `elide build` derives tasks from an elide.pkl manifest and runs them in dependency order: targets, options, the build cache, build state and timing.

`elide build` derives tasks from the source sets and artifacts in `elide.pkl` and runs them in dependency order.
Tasks with unchanged inputs are skipped or restored from the cache.

## Run a build

```bash
elide build                   # run the default group
elide build compile-java-main # run one task
elide build compile           # run a group
elide build clean             # the clean target
elide build --inspect         # list tasks and their options without running
elide build jvm-test --help   # one task's options and what they mean
elide build --no-cache        # ignore cached results and build state
elide build --offline         # no network: no dependency fetches, no shared cache
elide build --workers=4       # run at most four tasks at once
```

An argument names a task or group; a leading colon (`:compile-java-main`) is optional. A target that matches nothing
fails the build.

| Group | Alias | Tasks |
| --- | --- | --- |
| `default` | — | What a plain `elide build` runs. |
| `dependencies` | `deps` | Dependency resolution and install. |
| `compilation` | `compile` | Source compilation. |
| `testing` | `test` | Tests; also run by `elide test`. |

Some tasks take options, listed by `--inspect`:

```bash
elide build test --select=class:com.example.SomeTest
elide build run --args="a b c"
```

In a [workspace](https://elide.help/docs/elide-pkl-reference#workspaces), prefix the project: `core:compile-kotlin-main`, `core:compile`,
or `core` for its default tasks. Unprefixed targets resolve in the current project, then the root. `--inspect`
groups tasks by project, and progress rows name it (`[core] Compiling Kotlin sources`).

A project can add groups — and tasks — of its own, including tasks that run ahead of the built-in ones: see
[Build extensions](https://elide.help/docs/toolchain/build-extensions).

## Options

Options are `--name` or `--name=value`, anywhere among the targets. Arguments after `--` go to the process a task
launches. `--inspect` lists every task's options; `elide build <task> --help` describes one task's own.

| Option | Effect |
| --- | --- |
| `--inspect`, `-i` | List tasks and their options without running. |
| `--no-cache` | Ignore the cache and local build state; every task runs. |
| `--offline` | No dependency downloads and no shared cache. |
| `--workers=N` | At most `N` concurrent tasks (default: processor count). `=` is required. |
| `--insights=FILE` | Run an [insight script](https://elide.help/docs/api-insight) in tasks that run programs; repeatable. |
| `-f NAME[=VALUE]` | Build flag, read as `build.flags`; see [Build Flags](https://elide.help/docs/toolchain/build-flags). |

Options are checked against the tasks before anything runs. An unknown option, a missing or unexpected value, or a
repeated option fails:

```text
Unknown option '--worker', did you mean '--workers'?
Option '--workers' requires a value, as '--workers=<value>'
```

Global flags (`-p`, `-v`, `--debug` and the rest) are exempt. Task options:
[Dependencies](https://elide.help/docs/toolchain/dependencies#maven-options), [Testing](https://elide.help/docs/toolchain/testing#options).

## Build cache

The **local** tier is always on: `~/.elide/cache/entries-v2`, one directory per key with a record and a tar of the
outputs. Restores are checked against the recorded fingerprint. Entries in an older format miss.

The **shared** tier is an HTTP cache, set up by environment variables:

| Variable | Meaning |
| --- | --- |
| `ELIDE_REMOTE_CACHE` | Endpoint URL. Unset: no shared tier. |
| `ELIDE_REMOTE_CACHE_TOKEN` | Bearer token; takes precedence over basic auth. |
| `ELIDE_REMOTE_CACHE_USERNAME`, `ELIDE_REMOTE_CACHE_PASSWORD` | Basic auth; user info in the URL also works. |
| `ELIDE_REMOTE_CACHE_PUSH` | Upload this build's outputs (default `true`). |
| `ELIDE_REMOTE_CACHE_PULL` | Download misses (default `true`). |
| `ELIDE_REMOTE_CACHE_POLICY` | `eager` (default) transfers every entry; `smart` transfers only when it saves time. |

- Local is checked first; remote hits are stored locally.
- Typical setup: CI pushes; developers set `ELIDE_REMOTE_CACHE_PUSH=false`.
- Unreachable, slow or unreadable remote entries are misses. Three transport failures in a row turn the tier off for
  the rest of the build. HTTP errors such as `404` are not transport failures; a rejected credential warns once.
- The build fails on a non-`http`/`https` URL, a password without a username, a non-boolean `PUSH`/`PULL`, or
  credentials over plain `http` to a non-loopback host.
- The cache serves executable outputs: use `https`, require authentication for writes, and limit who can write.
- `--offline` turns off the shared tier for one run; `--no-cache` turns off caching.

### `.eliderc` and the `smart` policy

Under `smart`, an entry is downloaded only when that is cheaper than rebuilding, and uploaded only when the upload
costs less than the work it saves. Costs come from `.eliderc` (`java.util.Properties` syntax): `~/.eliderc`, then
`<project>/.eliderc` over it. It describes the machine, so do not commit it.

```properties
# ~/.eliderc
network.downlink = 200mbps
network.uplink = 20mbps
network.latency = 250ms
network.stability = good        # good | fair | poor
machine.compute = fast          # slow | normal | fast, or a multiplier such as 1.5

cache.remote.policy = smart     # smart | eager (default)
cache.remote.upload-budget = 2.0
cache.remote.min-build = 500ms  # work cheaper than this is never published
```

`Mb/s` and `mbps` are megabits; `MB/s` is megabytes. Unreadable values warn and use the default.
`ELIDE_REMOTE_CACHE_POLICY` overrides `cache.remote.policy`. Under `eager`, the file has no effect.

## Build state

| File | Role |
| --- | --- |
| `.dev/elide.lock.v2.bin` | Portable per-task fingerprints, shareable across machines. Not a dependency lockfile. |
| `.dev/elide.build.bin` | Machine-local per-task fingerprints. |
| `.dev/elide.filehash.bin` | File digests keyed on size, times, inode and mode, so unchanged files are not re-read. |

- Binary files; do not edit.
- There is no dependency lockfile for every ecosystem; npm writes `aube-lock.yaml`.
- In a workspace, state is stored in the root's `.dev/` for every member; outputs stay in each project's `.dev/`.
- `--no-cache` ignores the local state file but still reads the shared one.
- `elide install` reuses resolution while the dependency list is unchanged; `--fresh` downloads every dependency
  again, refreshing the global cache.
- Each compiler's arguments are written beside its output as `<tool>.args`, one per line.

See [The .dev directory](https://elide.help/docs/toolchain/development-directory) for generated files, workspace paths and cleanup.

## Time a build

Each task prints its duration. For more:

```bash
elide build -v                                                # everything, at debug
ELIDE_LOG=dev.elide.tooling.build.telemetry=debug elide build # only the timings
```

```text
[debug] dev.elide.tooling.build.telemetry stage graph took 590us
[debug] dev.elide.tooling.build.telemetry op compile-kotlin-main · up-to-date (up-to-date) took 420us
[debug] dev.elide.tooling.build.telemetry task compile-kotlin-main (skipped: up-to-date) took 12.6ms
```

Regions are a **stage** (configuration, graph, execution, state save), a **task**, or an **operation** on a task
(`prepare`, `fingerprint`, `up-to-date`, `execute`, cache lookups and stores). Work inside `execute` is not split
out.

`ELIDE_BUILD_TELEMETRY` writes a local [Chrome trace](https://perfetto.dev/docs/visualization/large-traces) when
the build ends; nothing is uploaded:

```bash
ELIDE_BUILD_TELEMETRY=1 elide build             # .dev/telemetry/build-<stamp>.trace.json
ELIDE_BUILD_TELEMETRY=profile.json elide build  # a file you name
ELIDE_BUILD_TELEMETRY=/tmp/traces elide build   # a directory you name
```

Open it in [Perfetto](https://ui.perfetto.dev), `chrome://tracing` or [speedscope](https://speedscope.app), or:

```bash
jq -r '.traceEvents[] | select(.ph=="X") | "\(.dur/1000 | floor)ms \(.cat) \(.name)"' \
  .dev/telemetry/build-*.trace.json | sort -rn | head
```

### Restore observations and session correlation

Servers supporting Elide restore receipts may return `x-elide-recall-receipt`
when an archive download requests `x-elide-recall: 1`. The client reports the
outcome to `outcomes` beneath the configured cache endpoint after extraction
and validation. Downloading bytes alone does not count as a successful restore;
subsequent local hits do not duplicate remote observations. Reporting is best
effort and does not decide whether a build succeeds.

`ELIDE_STUDIO_SESSION_ID` supplies bounded request correlation metadata to the
configured build-cache endpoint and the HTTPS Maven Pkgst origin. It is not a
credential and does not change action keys. Authenticated or session-correlated
build-cache requests do not follow redirects, preventing metadata leakage to
another origin.

Restore reports include the original task duration as a bounded estimate, so a
server can compare likely rebuild cost with measured restore cost. That estimate
is not a billing measurement or proof of time saved.
