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

<a id="the-three-workflows"></a>

# The three workflows

Every package and platform repository has the same three workflows. Each answers
one question, and none of them changes the repository.

| Workflow | Trigger | What it proves | Token access |
| --- | --- | --- | --- |
| ` ci.yml ` | Pull request; push to the default branch | Scripts pass on ` roc-stable `. The working tree and its examples pass on the latest nightly. A platform’s committed host bindings match that nightly. A Roc package has no ` ci.yml `: its release dry run proves the same things. | ` contents: read ` |
| ` release.yml ` | Manual dispatch; pull request as a dry run | The exact bundle, examples archive and documentation proposed for publication work. A Roc package’s ` release.yml ` is a thin caller of the shared ` release-package.yml `. | ` contents: read `. The publish job alone has ` contents: write `, behind a protected environment. |
| ` nightly.yml ` | Daily schedule; manual dispatch | The latest published release and its examples archive work with the latest nightly. | Test jobs ` contents: read `; the report job ` issues: write ` |

Rules that hold in all three:

- Every ` uses: ` reference is a full commit SHA.


- Top-level ` permissions ` is ` {} `, and each job states what it needs.


- Checkout sets ` persist-credentials: false `.


- A job that runs repository code has no write access. A job with write access
  runs no repository code.

- The nightly tag is resolved once, in the first job, and passed to every other
  job as the ` nightly-tag ` input of ` roc-lang/setup-roc `. Matrix jobs never
  each resolve "latest" for themselves, or one run could test several
  compilers.

- Each job calls a ` blueprint run <task> ` and holds no logic of its own.


Workflows beyond these three, such as a documentation site or scheduled fuzzing,
are described in
[Optional workflows](optional-workflows.md#optional-workflows).

<a id="risk-release-evidence"></a>

## Risks and controls for release evidence

A workflow can publish a different source revision or rebuild assets after
testing, and a retry can combine files from unrelated attempts. Preserve source
and artifact identity across the release. Build and test the selected commit,
pass exact files through the run, and let a separately authorized publisher
verify the inventory and downloaded asset hashes. Interrupted-publication rules
refuse ambiguous replacement. Exercise dry runs and publisher failure transitions.
These controls bind the delivered bytes to the selected evidence; they do not
prove an independently reproducible build or the trustworthiness of a compiler.

<a id="continuous-integration"></a>

## Continuous integration

` ci.yml ` tests the working tree.

1. Set up Blueprint and install the latest nightly, as in
   [Use the environment in CI](environment.md#use-the-environment-in-ci).

2. Run ` blueprint run check `.


[What runs on every pull request](testing.md#what-runs-on-every-pull-request)
lists what the ` check ` task covers.

Keep routine CI and release dry runs fast: benchmark and fuzz programs build
and run their explicit smoke mode, with at most one second of workload execution
per target. Compilation has its own budget. Sustained searches and measured
benchmarks run only in schedule/manual jobs with explicit event guards; see
[Heavy runs stay out of pull-request CI](testing.md#heavy-runs-stay-out-of-pull-request-ci).

A [Roc package](overview.md#a-pure-roc-package) has no ` ci.yml `. The
[shared release workflow](#the-shared-release-workflow-for-a-pure-package)
runs ` blueprint run check ` and the examples on every pull request as part of
its dry run, and its two job names are the required status checks.

A repository whose code can behave differently by target runs it as a matrix,
one job per runner, and ends with a single aggregate job that requires every
matrix job to have succeeded. The aggregate job’s name is the required status
check, so the ruleset does not need editing when the matrix changes.

A platform adds the host-bindings check described in
[Host bindings](platforms.md#host-bindings). It runs here and nowhere else.

A pull request can fail because a new nightly arrived, with no fault in the
change itself. Compare with the latest ` nightly.yml ` run and with the default
branch before treating a failure as a regression in the pull request.

<a id="release"></a>

## Release

` release.yml ` builds what a release would contain, tests those exact bytes, and
publishes them once. A maintainer dispatches it from the default branch with a
version. A pull request runs the same build and tests as a dry run and publishes
nothing.

<a id="the-shared-release-workflow-for-a-pure-package"></a>

### The shared release workflow for a Roc package

A Roc package keeps no release logic of its own. Its ` release.yml ` calls
roc-automation’s ` .github/workflows/release-package.yml ` at a full commit SHA:

``` yaml
name: Release

# A pull request builds and tests the release under a version that is never
# published. Dispatch from the default branch with a version to publish it.
on:
  pull_request:
  workflow_dispatch:
    inputs:
      version:
        description: Version to publish, such as 0.1.0 or 0.1.0-rc1; empty runs the dry run
        type: string
        default: ''
      nightly-tag:
        description: Exact Roc nightly for a diagnostic run; empty resolves the latest
        type: string
        default: ''

permissions: {}

jobs:
  Release:
    permissions:
      contents: write
      id-token: write
      attestations: write
    uses: lukewilliamboswell/roc-automation/.github/workflows/release-package.yml@8f73710f90f942f3097821b189bbf341d305cd55
    with:
      version: ${{ inputs.version || '' }}
      nightly-tag: ${{ inputs.nightly-tag || '' }}
      check-task: check-package
```

The optional numeric ` build-timeout-minutes ` input defaults to ` 30 ` and accepts
integers from ` 1 ` to ` 360 `. Set it high enough for the full configured checks and
release assembly, for example ` 75 ` for a large parser conformance suite. The
request guard rejects invalid values before candidate execution. A timeout or
failed build prevents testing and publication; raising the limit changes no
checks or token permissions.

The shared workflow runs three jobs. **Build and test the source** resolves the
latest nightly once, runs the package’s ` check ` task, checks version
sufficiency against the latest release, runs the working-tree examples, and
builds the bundle, the examples archive, the API documentation archive and the
notes. **Test the release candidate** checks out no package source: it downloads
those exact files and runs the archived examples against the bundle over a
loopback address. **Publish the release** runs only for a dispatched version on
the default branch, in the ` release ` environment; it attests the tested files
and hands them to [` actions/publish-release `](actions.md#publish-release).

The bundles, the archives, the notes and both example runs are one utility,
[` release-package `](actions.md#release-package), a static executable from
a roc-automation tools release that the repository’s Blueprint fetches by
digest and names as the ` release-package ` task. The workflow checks out its
own repository with ` job.workflow_sha ` for the shared actions it uses, so the
caller’s SHA fixes the workflow and the lock fixes the utility; the package
compiler is the latest nightly, installed by the workflow. A maintainer runs
the same task locally with that compiler on ` PATH `:

``` sh
blueprint run release-test -- test-examples               # the working tree's examples against tests/spec.json
blueprint run release-test -- test-examples --update      # record what they print now, as a reviewable diff
blueprint run release-package -- build 0.0.1-dryrun       # the bundles, archives and notes, into dist/
blueprint run release-test -- test-archive                # the archive in dist/ against the bundles in dist/
```

The inputs are the version, an exact nightly for a diagnostic run, the name of
the Blueprint task that checks the package and one program to run before it for
a prerequisite the runner lacks, an SBOM author with an optional composition
task and supplier-imports record when the release publishes sidecars, and a
` manual ` switch with an optional task that writes generated manual sources
before the PDF is rendered. With the
calling job named ` Release `, the required status checks are
` Release / Build and test the source ` and ` Release / Test the release candidate `.
The caller declares no concurrency group: the shared workflow holds one release
at a time per repository.

A platform keeps a ` release.yml ` of its own, described in
[A platform’s release workflow](#a-platform-release-workflow).

<a id="adopt-the-shared-workflow-in-an-existing-repository"></a>

#### Adopt the shared workflow in an existing repository

The move from a copied release workflow is the same in every package so far.

1. A ` check ` task in ` Blueprint.roc ` that runs the package’s own checks, and the
   ` release ` and ` release-test ` environments and tasks from
   [the utility’s section](actions.md#release-package), followed by
   ` blueprint update ` for the lock. A check that takes longer than a few
   minutes, such as fuzzing or conformance suites, stays in the repository’s own
   ` ci.yml `; pass ` check-task: '' ` then, so that the release dry run stays fast.

2. Every example depends on the package by relative path and names no ` roc `
   compiler; the release notes record the compiler instead. A ` .roc ` file under
   ` examples/ ` without an ` app ` header is a shared module. An example that
   prints the clock or anything else that varies takes an argument or input
   that makes a case deterministic: a skipped case never passes.

3. ` release-package test-examples --update ` writes ` tests/spec.json ` with one
   case per application and records what it prints; review that diff, then add
   cases.

4. Notes in ` docs/releases/<version>.adoc ` open with ` = <name> <version> ` and
   use ` {package-url} `, plus ` {<dir>-url} ` for each further bundle.

5. The thin ` release.yml `, then delete the scripts and workflow jobs that
   bundled, archived, tested examples, rendered notes or published, and ` ci.yml `
   when ` check-task ` runs the checks.

6. SBOM sidecars need ` sbom/composition.json ` and an author; a
   manual PDF needs the roc-docs renderer behind a ` docs-path ` task. A
   repository without either leaves those inputs unset and keeps its own
   documentation workflow.

7. Update the ruleset’s required checks to the two shared job names, and after
   the first green dry run, a maintainer dispatches the next release.

<a id="a-platform-release-workflow"></a>

### A platform’s release workflow

A platform’s host is built with its own toolchain, and its bundle holds that
host for every target, so its ` release.yml ` builds them itself and hands the
rest to the same utility, as
[A platform release](actions.md#release-package-platform) describes.
It has the shape of [one builder and a matrix of runners](platforms.md#cross-compile-once-run-on-every-target):

1. **Build**, on one Linux runner: build the host for every target, then
   ` release-test -- test-examples `, the repository’s bundle command with
   ` --output-dir build/platform `, and
   ` release-package -- build "$VERSION" --platform-bundle build/platform `.
   Upload ` dist/ `.

2. **Test the release candidate**, one job per supported system, Windows
   included: install Blueprint with
   [` actions/setup-blueprint `](actions.md#setup-blueprint) and the nightly
   the build resolved, download ` dist/ `, and run
   ` release-test -- test-archive `. Nothing is rebuilt.

3. **Publish**, as for a package: from the default branch, in the ` release `
   environment, attest the tested files and hand them to
   [` actions/publish-release `](actions.md#publish-release), with no
   checkout of the platform’s source.

The repository keeps only what is its own: the host build, the target layout,
the linker inputs and the bundle command with its inventory. Its examples,
` tests/spec.json ` and notes follow the package rules, with ` {platform-url} `
for the platform’s URL.

<a id="what-a-release-contains"></a>

### What a release contains

One immutable GitHub release per version holds:

| Asset | Purpose |
| --- | --- |
| The bundle, under the filename ` roc bundle ` emits | What users depend on. The filename ends in its content hash, which is never changed. [Several bundles in one release](#several-bundles-in-one-release) says when the filename also carries a name. |
| ` <name>-examples-<version>.zip ` | Examples pinned to this release’s bundle, with their expected output. |
| ` <name>-docs-<version>.zip ` | The generated API reference and site for this version, so the documentation of any release can be restored. |
| ` <name>-<version>.pdf ` | The manual, where the repository has one. |
| Release notes | An authored account of what changed, with the real bundle URLs and the compiler versions used. |
| ` <asset-name>.spdx.json ` or ` <asset-name>.cdx.json ` | Machine-processable component inventory bound to each deliverable’s digest, with target-specific relationships and declared coverage. See [the SBOM contract](software-bill-of-materials.md#publish-and-consume-software-bills-of-materials). |
| Signature/attestation evidence | Authenticated SBOM statements and build provenance, with expected signer, source and subject identities. These establish different evidence. |

Managing releases carefully is a large part of what makes a package dependable.
A user meets a release through three things: its version number, its notes, and
the URL they paste into a header. Each is handled deliberately.

<a id="choose-the-version-with-roc-bump"></a>

### Choose the version with ` roc bump `

A version number is a promise about compatibility, and ` roc bump ` checks the
promise against the code. It compares the public API of the package with a
previous release and reports the semantic-version bump the difference requires:
patch, minor or major.

``` sh
# what bump does this change need, and what is the next version?
roc bump --old <previous release bundle URL> package/main.roc

# in release.yml: fail unless the chosen version bumps far enough
roc bump --old <previous release bundle URL> --expect 0.13.0 package/main.roc
```

- Run it before choosing a version, so the number follows from the change and
  not from habit.

- ` release.yml ` runs it with ` --expect ` in its validate step, against the
  latest release for same-major requests, and stops when the requested version understates the change.
  A breaking change can then never go out as a patch.

- A requested major greater than the latest final major already satisfies the
  lower bound for every possible API change. The released ` release-package `
  utility validates both versions and accepts that proof before attempting
  extraction. It does not compute an API diff in that case. Same-major releases
  still require a successful comparison, including when extraction fails.

- This version proof does not establish that the candidate compiles or works.
  The configured package checks, working-tree examples, exact candidate tests
  and SBOM checks remain separate release requirements. Malformed or unreadable
  latest-release metadata cannot authorize the proof.

- A repository with several packages runs it once per package root.


- It compares only the modules the package header exposes. A change in behavior
  behind an unchanged signature is still the maintainer’s call, and belongs in
  the notes.

A package version is independent of any compiler version. A changed package is
always a new version. Nothing is ever republished under an existing version.

<a id="write-the-release-notes"></a>

### Write the release notes

Each release has its notes in ` docs/releases/<version>.adoc `, for example
` docs/releases/0.12.0.adoc `. They are committed in a pull request before the
release is dispatched, and the workflow refuses to release a version whose
notes file is missing.
The workflow publishes the notes with the release. Keep the manual focused on
the current interface and procedures; link readers to the published releases
for version-specific changes.

The notes are an authored narrative, written for a user deciding whether and
how to upgrade. They are not a list of commits or merged pull requests.

- Open with what this release is for, in a sentence or two.


- Describe what changed in terms of what a user can now do, or must now do
  differently.

- For a breaking change, show the code before and after.


- Say what was fixed, naming the visible symptom.


- Thank contributors, and link issues where a reader may want the background.


**docs/releases/0.12.0.adoc**

``` asciidoc
= roc-example 0.12.0

This release adds streaming decoders and removes the deprecated `Table.render`.

== Use this release

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

== Streaming decoders

...
```

The bundle URL cannot be written by hand, because it contains the content hash
of a bundle that does not exist until the release builds it. The notes
therefore use a placeholder, and the workflow fills it in:

- ` {package-url} ` stands for the released bundle’s URL, and ` {platform-url} `
  for a platform’s. A release with several bundles has one placeholder for
  each, named after the package, such as ` {tzdb-url} `.

- After building the bundles, ` release.yml ` renders the notes with each
  placeholder set to the real URL, tag and hash. A placeholder that is left
  unresolved, or one the notes never use, fails the release.

- The rendered notes become the body of the GitHub release, followed by a
  section headed "Compilers" that holds the output of ` roc version ` and
  ` roc-stable version `. That record is the statement of what the release was
  built and tested with; no header pin makes it.

- A dry run renders the notes too, so a mistake in them fails on the pull
  request. It renders one file: the notes for the highest version under
  ` docs/releases/ `, which are the notes of the next release whenever a pull
  request adds or edits them. It asks nothing of GitHub to choose that file.

The committed file keeps its placeholders. It is never rewritten after the
release, and no follow-up pull request is needed.

<a id="the-order-of-a-release"></a>

### The order of a release

**A release tests exact bytes, then publishes once**

![A release tests exact bytes, then publishes once](images/diagram-7329b9cb18489873cfaabed01172959f5f5ed418723e7c2b64d3c95b1003a84a.svg)

1. **Validate.** Require the default branch. Reject a version whose tag or release
   already exists. Require ` docs/releases/<version>.adoc `. Check the version
   with ` roc bump --expect `. Record the event commit and use that SHA in every
   later job.

2. **Check.** Run ` blueprint run check ` on that commit.


3. **Build.** Build the bundle with the latest nightly, then the examples archive
   with the real release URL, the documentation archive, and the manual.
   Generate and locally validate the SBOM sidecars from the resolved build and
   final artifacts. Render the release notes with the real bundle and SBOM URLs.
   Upload them as workflow artifacts.

4. **Test.** On every supported target, download those artifacts and test them as
   described in
   [Build and test the exact archive](examples.md#build-and-test-the-exact-archive).

5. **Publish.** A separate job, with ` contents: write ` and a protected
   environment, downloads the tested artifacts. It checks out nothing and runs no
   repository script. It hands the version, the rendered notes and the asset
   files to
   [` actions/publish-release `](actions.md#publish-release), which stages a
   draft at the tested commit, verifies every uploaded asset against the tested
   file, and then publishes.

6. **Update the site.** For a final release, rebuild the documentation site so
   that it points at the new release, and deploy it; see
   [What the site points at](optional-workflows.md#what-the-site-points-at).
   A release candidate does not change the site.

The publish job consumes the artifacts of its own run. It never rebuilds, and
never resolves a moving branch.
SBOM generation and validation belong to the read-only build task and its PR
dry run; [` actions/sbom-sidecars `](actions.md#sbom-sidecars) implements
them with the pinned Blueprint tool, and
[` actions/fetch-dependency-sboms `](actions.md#fetch-dependency-sboms)
chains the suppliers' documents in. The shared publication action does not
implement those steps; it uploads the supplied SBOM files as inert assets and
verifies their transferred bytes. Before attestation, the shared package
workflow also rechecks SBOM subject/sidecar bindings against the transferred
envelope and supported Blueprint manifests, using reviewed controller code and
the build job’s producing attempt. Pass the asset list the sidecar action
returns to the upload, the attestation and the publisher, so a missing sidecar
cannot hide behind a wildcard. Remote advisory rescans run separately from routine PR checks.

Enable immutable releases in the repository, so that a published release cannot
gain, lose or replace an asset. Hash checks remain necessary even with immutable
hosting.

<a id="several-bundles-in-one-release"></a>

### Several bundles in one release

A repository that publishes several packages or platforms builds one bundle for
each, in the same run, under the same version, and one examples archive whose
specification names every bundle URL. The release notes list every bundle URL
by name. All of them are tested before any of them is published.

The shared release workflow derives the bundles from the examples: every
package root an example depends on by relative path is bundled, tested and
published, and a package no example exercises is not released. The repository’s
own package is ` package/main.roc `; another is ` <dir>/package/main.roc `, such as
` tzdb/package/main.roc `. A release with one bundle keeps the bare ` <hash>.tar.zst `
name. With several, the repository’s own package becomes ` <name>-<hash>.tar.zst `
and the other ` <name>-<dir>-<hash>.tar.zst `, and its placeholder in the notes
is ` {<dir>-url} ` beside ` {package-url} `. Every tracked file in a package’s
directory goes into its bundle, modules and data alike.

Two bundles under one tag need different names as well as different hashes.
Roc identifies a package by its URL with the version and the content hash
removed. Two files named only ` <hash>.tar.zst ` under one tag are therefore one
package with two contents, and Roc refuses the pair as soon as one depends on
the other:

``` text
The same package version is being served with two different content hashes
```

Give each bundle a prefix that names its package and ends in a hyphen, such as
` example-tzdb-<hash>.tar.zst `. Roc reads the hash as the run of base58
characters at the end of the filename, so the prefix must end in a character
that is not base58, and a hyphen is one. The hash itself is never changed.

- At most one bundle in a release may go without a prefix. In a repository that
  starts with several packages, name every one.

- The prefix is part of the package’s identity. Adding, removing or changing it
  makes a different package as far as Roc is concerned, so choose it before the
  first release, and treat a change to it as a breaking release.

- ` roc bundle ` writes ` <hash>.tar.zst `. The script that builds the release
  copies that file to its prefixed name, and the prefixed file is what is
  tested and published.

- ` roc bundle ` resolves the dependencies of the package it bundles. A package
  that names a sibling by its final release URL is bundled after that sibling,
  with the sibling’s bundle already in the package cache, because the URL does
  not exist until the release is published.

- ` roc unbundle ` accepts only the bare ` <hash>.tar.zst ` name. A script that
  unpacks a prefixed bundle copies it to that name first.

<a id="release-candidates"></a>

### Release candidates

A release candidate, such as ` 0.12.0-rc1 `, is a prerelease published for users
to test before the maintainers commit to the release. It answers the question
the workflows cannot: does this version work in real projects, and is the API
right?

- Publish a candidate through ` release.yml ` with a prerelease version. It
  follows every step above and holds the same assets, so what users test is
  built exactly as the final release will be.

- Mark it as a prerelease on GitHub. It is never the repository’s latest
  release.

- Announce it where users will see it, and say what feedback is wanted and for
  how long.

- Fix what the feedback finds on the default branch, and publish ` -rc2 `. A
  candidate is immutable like any other release and is never amended.

- When a candidate has stood without change, release the final version from
  the same commit. Its bundle is a new artifact under a new tag, and it goes
  through the release workflow in full.

Everything that speaks of "the latest release" means the latest release that is
not a prerelease. ` nightly.yml ` tests that one, and the documentation site
links to that one. A candidate is found from the releases page and from its
announcement.

<a id="recover-an-interrupted-publication"></a>

### Recover an interrupted publication

If publication stops, inspect exactly what exists before retrying. The publish
job’s error says which of these states it left.

- **A draft and no tag.** Nothing is public. The error names the draft, which
  holds whatever was uploaded before the failure. Look at it to see how far the
  job got, delete it, and re-run the failed publish job of the same workflow
  run. That job publishes the files the run already tested. The publish action
  never adopts a draft it finds, so the draft is deleted first.

- **A published release.** It is immutable and is never repaired. If it is
  complete, the version is released and nothing is re-run. If it is not, publish
  a new version.

- **Unknown.** When the answer to the publish request was lost, look at the
  releases page and treat what is there as one of the two states above.

Changed bytes are always a new version. Do not publish a draft by hand, replace
an asset, or rebuild from a moving branch. If the run’s artifacts have expired,
delete the draft and dispatch the release again from the default branch. The
version was never published, so it is still free, and the new run builds and
tests its own files.

<a id="nightly-compatibility"></a>

## Nightly compatibility

` nightly.yml ` answers one question each day: does the release users download
today still work with today’s compiler? A failure means the package or platform
needs a patch release.

Use the pinned shared workflow in
[Shared published-release compatibility](nightly-compatibility.md#shared-published-release-compatibility), rather
than maintaining a local copy of selection, test execution and reporting.
Compiler-pin updates remain manual work during downstream pull requests and do
not belong in this workflow. Warnings from successful compilation stay visible
and nonfatal; skipped or incomplete evidence never establishes a passing result.

**The inputs to one nightly run and its two outcomes**

![The inputs to one nightly run and its two outcomes](images/diagram-91d277e31466e5c84190f6c3b5d19992398286335c0b71840dfe34f2c3cfd357.svg)

- Resolve the latest nightly tag and the latest release once. Accept both as
  optional dispatch inputs, so a maintainer can re-run a past combination.

- Skip prereleases when choosing the latest release.


- Download that release’s examples archive and run it unchanged, comparing with
  the expected output inside the archive. Do not rebind it to the working tree,
  and do not test the default branch’s examples here: new APIs on the default
  branch are not evidence about a release.

- Use the shared runner to read the archived specification and own every
  compiler invocation. Default-branch code may only provision native prerequisites
  in the read-only test job.

- Run on every target the release is tested on: the one runner of a
  [Roc package](overview.md#a-pure-roc-package), or the same matrix
  as ` ci.yml `.

- A platform’s host bindings are not checked here. A released host is fixed;
  what matters is whether its applications still build and run.

The archive is immutable, so a failure is never a reason to change it. The fix
is a patch release that works with the new compiler.

<a id="reporting"></a>

### Reporting

A separate job reports the result by calling
[` actions/nightly-report `](actions.md#nightly-report), which keeps one
tracking issue: opened on the first failure, commented on by each later
failure, and closed by the next pass. The action defines which issue that is and
what each outcome does. The workflow supplies the rest:

- Reporting is considered after a failed test too, and holds ` issues: write `
  and nothing else. Only an established success or failure reaches the issue
  action; unknown evidence leaves the issue alone.

- It checks out nothing and runs no repository or archive code.


- The shared read-only aggregator verifies matching evidence from every matrix
  target and the current run attempt. It supplies only a constrained outcome,
  nightly tag and release tag to the report action, never example output.

- A failed test-job result alone does not establish incompatibility. Setup,
  cancellation, missing evidence, skips and timeouts are distinct outcomes and
  cannot close an issue. A complete pass requires every target’s tests to pass.
  Manual historical override runs do not modify the latest-pair issue.

A repository with no final release or no canonical examples archive has no
compatibility result: selection fails with a diagnostic and does not call the
issue action. A missing test is never displayed as a passing nightly.

This workflow never opens a pull request, commits, merges, or publishes.

