> Documentation version: 3. [HTML page](repository.html)

<a id="lay-out-the-repository"></a>

# Lay out the repository

A consistent layout lets the same checks, scripts and workflows apply to every
repository, and lets a maintainer who knows one repository find their way in
another.

<a id="risk-source-boundaries"></a>

## Risks and controls for repository source boundaries

A test can accidentally import a published dependency instead of the changed
working tree, while source automation can accidentally depend on the broken
product it must diagnose. Make source roles explicit. Product examples use
relative working-tree dependencies; source tooling uses independently selected
released dependencies and a fixed compiler. Header and source-policy checks
verify those conventions, and behavioral tests verify the product itself.
Static policy establishes declared structure, not which code paths execute or
whether a package is correct.

<a id="the-layout"></a>

## The layout

``` text
Blueprint.roc        development environment and named tasks
Blueprint.lock       pinned inputs
package/main.roc     a package; or platform/main.roc for a platform
examples/            applications that depend on the working tree
scripts/*.roc        executable automation entry points
scripts/src/*.roc    modules shared by those scripts
tests/               package-specific cases and expected output
docs/*.adoc          the manual, where the repository has one
```

A repository that publishes several packages or platforms gives each its own
root directory, such as ` package/ ` and ` tzdb/package/ `, or ` platform-web/ ` and
` platform-gui/ `. Each root is bundled, versioned and released together with the
others in one release; see
[Several bundles in one release](workflows.md#several-bundles-in-one-release).

<a id="where-a-compiler-pin-belongs"></a>

## Where a compiler pin belongs

The ` roc: "..." ` header field appears in exactly one place: the scripts under
` scripts/ `, where it names the same release as ` roc-stable `. Nothing else is
pinned. The package, a platform, the examples and every other Roc application
carry no ` roc ` field, and the repository has no ` .roc-version ` file and no
` .github/roc-nightly.json `.

The rule for the package applies to platforms as much as packages. ` roc check ` exits with an error
on a warning, and a dependency whose header pins a different nightly produces a
version-mismatch warning. A pinned platform therefore fails every application
that floats, and a package cannot float until every platform its examples use
has dropped the field.

The check script enforces both halves. It fails when a ` roc ` field or
` .roc-version ` appears outside ` scripts/ `, and it fails when a script’s pin
differs from the tooling compiler; see
[Keep the script pins in step](#keep-the-script-pins-in-step).

<a id="roc-applications-in-the-repository"></a>

## Roc applications in the repository

Every Roc application in the repository depends on the working tree by relative
path. Pull-request CI checks them with the latest nightly compiler. That covers
` examples/ ` and also anything outside it: fuzz targets, benchmarks,
documentation examples, test cases, and a site generator.

**In the repository: the package is a relative path**

``` roc
app [main!] {
	cli: platform "https://github.com/roc-lang/basic-cli/releases/download/<version>/<hash>.tar.zst",
	ansi: "../package/main.roc",
}
```

The package is a relative path. Every other dependency, including the platform,
is an immutable release URL with its content hash.

These are not what the nightly workflow tests. They change with every pull
request and may use APIs that no release has. The nightly workflow runs the
examples archive attached to the latest release. In the archive the same example
names that release’s exact bundle URL, and nothing else differs:

**In a release’s examples archive: the package is that release’s bundle**

``` roc
app [main!] {
	cli: platform "https://github.com/roc-lang/basic-cli/releases/download/<version>/<hash>.tar.zst",
	ansi: "https://github.com/<owner>/roc-ansi/releases/download/<tag>/<hash>.tar.zst",
}
```

The release workflow writes the second form when it builds the archive; the
repository only ever contains the first. See
[The examples archive](examples.md#the-examples-archive).

The single exception is ` scripts/ `. A script never depends on the repository’s
own package by relative path, because a script must keep working while the
package is broken.

An example that needs more than one file is a directory: ` examples/report/ `
holds ` main.roc `, its sibling modules and any data files it reads. A local
helper package that several examples share sits beside them and is referenced by
relative path.

<a id="use-shared-utilities-through-blueprint-tasks"></a>

## Use shared utilities through Blueprint tasks

Use a released executable for an operation shared across repositories, with
its recipe and named task in Blueprint. Local use and CI invoke the same task.
The
[shared utility skeleton](shared-utilities.md#share-automation-through-executable-utilities)
shows a working consumer and the producer boundaries. A consumer of a prebuilt
tool does not need that tool’s source dependencies or implementation compiler.

Write a local script only for behavior or composition specific to the product.
Keep common validation, example execution and release orchestration in their
shared utilities rather than copying helper modules between repositories.

<a id="write-automation-as-roc-scripts"></a>

## Write product-specific automation as Roc scripts

A source entry point kept under ` scripts/ ` is an executable Roc application:

``` roc
#!/usr/bin/env roc-stable
app [main!] {
	cli: platform "https://github.com/roc-lang/basic-cli/releases/download/<version>/<hash>.tar.zst",
	roc: "nightly-<date>-<commit>",
}
```

- Start with the ` roc-stable ` shebang and commit the file as executable.


- Set the ` roc ` header field to the release that ` Blueprint.roc ` names as
  ` roc-stable `.

- Reference every dependency by immutable release URL with its content hash,
  and list the same URLs under ` RocPackages ` in ` Blueprint.roc `.

- Run package work with ` roc `, which is the latest nightly in CI and on a
  contributor’s machine. Never use ` roc-stable ` to check, test or bundle the
  package.

- Put pure logic in ` scripts/src/ ` modules with ` expect ` tests, and run those
  tests from the check script.

- Pass arguments after ` -- `, for example ` scripts/test_goldens.roc -- --update `.


- Declare every entry point as a ` Task ` in ` Blueprint.roc `.


Product orchestration scripts are written in Roc. Small platform adapters and
independent test harnesses may use the runtime their documented contract
requires. A script that only wraps one command is replaced by a Blueprint
` Task ` that runs that command directly.

<a id="keep-the-script-pins-in-step"></a>

### Keep the script pins in step

A script is written for one compiler and one set of dependencies. Someone who
runs it with a newer ` roc `, instead of through ` blueprint `, may find it broken
for reasons that have nothing to do with their change. The ` roc ` field makes
the compiler say so: a compiler other than the one the field names refuses the
file and names the release it targets, which points at the cause before anyone
debugs the script.

The field is useful only while it is true, so one cheap check keeps it true.
The check script reads the tag from the ` Command("roc-stable", ...) ` setting in
` Blueprint.roc ` and requires every file under ` scripts/ ` that has an ` app `
header to carry exactly that tag in its ` roc ` field. It reads text and runs no
compiler, so it costs nothing in pull-request CI.

Two things follow from the compiler enforcing the field:

- Format the scripts with ` roc-stable fmt `. ` roc fmt ` on another compiler
  rewrites the field to that compiler’s own release, and the check then fails.

- The field belongs only on a file that ` roc-stable ` runs. A program under
  ` scripts/ ` that the latest nightly builds, or that a compiler the repository
  itself produces builds, cannot carry it: that compiler would refuse the file.
  Keep such a program outside ` scripts/ ` where that is practical. Where it
  stays, the check script lists it by path as an exception and requires that it
  has no ` roc ` field at all.

` Blueprint.roc ` remains the authority. The header fields are copies that the
check holds to it, and
[Change the tooling compiler](maintenance.md#change-the-tooling-compiler)
changes them together.

<a id="the-check-script"></a>

### The check script

One ` check ` task owns everything a pull request must pass. It uses shared
checking utilities; a product-specific driver, conventionally
` scripts/check_all.roc `, can compose additional checks when needed. Its scope
includes the static checks in this chapter,
` roc fmt --check ` and ` roc-stable fmt --check `, the script tests, ` roc test ` on the package root, every
example and integration case, a smoke run of each benchmark and fuzz target,
and a dry-run build of what a release would publish.
[What runs on every pull request](testing.md#what-runs-on-every-pull-request)
describes each.

<a id="what-is-committed"></a>

## What is committed

| Committed | Not committed |
| --- | --- |
| Source, ` Blueprint.roc `, ` Blueprint.lock ` | ` .blueprint/ ` and anything else ` blueprint ` generates |
| Expected output and golden files under ` tests/ ` | Build output, bundles, archives |
| Generated data the package ships, with the recipe and inputs that produced it | Rendered API documentation and the built site |
| A platform’s generated host bindings | Native binaries, which are released independently and consumed through a content lock; see [No binary blobs in the repository](platforms.md#no-binary-blobs-in-the-repository) |

Generated data that is committed has a script that regenerates it and a check
that fails when the committed copy is stale. Rendered documentation is published
as a release asset and a Pages deployment, never committed.

<a id="what-goes-in-a-bundle"></a>

## What goes in a bundle

A bundle holds what a user of the package needs and nothing else: the package’s
Roc modules and any data files they import. Fuzz targets, benchmarks and test
modules stay outside the package root, so that ` roc bundle ` on the root produces
the release artifact with no exclusion list.

