> ## Documentation Index
> Fetch the complete documentation index at: https://docs-stg.outerlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Emit commands

> Record a named check, upload a proof artifact, record findings, report a step, or record a work item's acceptance criteria.

Every command on this page also takes `--url <url>` and `--app-id <id>`. Their defaults are in [Credential resolution](/reference/cli#credential-resolution). Each exits 0 on success and 1 with the reason on stderr otherwise. In CI, set the credential variables instead of running `login`. See [In CI](/reference/environment-variables#in-ci).

## outerlayer emit (a named check)

Record one named check's outcome on a work item.

```bash theme={"system"}
outerlayer emit <name> --item <number> [--result pass|fail]
                       [--link <url>] [--body <text> | --body-file <path>]
                       [--items <path>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--item <number>` | required | The work item, by its number on the factory. Required inside a recorded session too. |
| `--result <outcome>` | `pass` | `pass` or `fail`. With `--items` and no `--result`, the check fails when a check item fails. |
| `--link <url>` | none | Proof link, usually the CI run URL. An `http://` or `https://` URL with no whitespace, at most 500 characters. |
| `--body <text>` | none | One sentence recording your judgment. At most 2,000 characters. |
| `--body-file <path>` | none | Read the sentence from a file. `-` reads standard input. Pass `--body` or `--body-file`, not both. |
| `--items <path>` | none | A JSON array of [check items](/policy-and-validators#check-items), one per thing the check judged. `-` reads standard input. With no body, the sentence names each failing check item. |
| `--json` | off | Machine-readable output. |

The name starts with a lowercase letter, then lowercase letters, digits, `.`, `-` or `_`, up to 64 characters. A fail needs `--link` or a body. A pass needs neither. The command prints the recorded check's id.

```bash theme={"system"}
outerlayer emit smoke.pass --item 412 --link https://ci.example.com/run/42
outerlayer emit code-review --result fail --item 412 --body "The button should be red."
outerlayer emit acceptance-criteria --result pass --item 412 --link <the judge report's deep link>
```

`outerlayer init` writes two checks. A session, CI or a person records `code-review`. The runner records `acceptance-criteria` after a build ends ok. See [Policy and validators](/policy-and-validators#the-code-review-check).

Inside a recorded session, the check is stored as the session's. A session cannot record over a person's fail. A session not launched with `OUTERLAYER_WORK` is refused. A person's own pass or fail on one criterion's proof goes on that criterion's thread instead: see `outerlayer work comment` in [Work commands](/reference/cli-work).

Exit codes: 0 recorded, 1 refused or invalid.

See also: [Record a check](/attach-evidence#record-a-check).

## outerlayer emit artifact

Upload a proof artifact with a caption.

```bash theme={"system"}
outerlayer emit artifact <file> --caption <text> [--for <criterion-id>]
                                [--test <name[=path:line]>]...
                                [--replaces <ids>] [--pr <number>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--caption <text>` | required | One sentence saying what the artifact shows. At most 500 characters. |
| `--for <criterion-id>` | none | The acceptance criterion this proves. Its declared proof form must match the artifact's kind. |
| `--test <name[=path:line]>` | every test | With a JUnit XML file and `--for`, bind only this test, by name. Repeat it for each test. |
| `--replaces <ids>` | none | Retire earlier artifacts. One id, or up to 20 comma-separated. |
| `--pr <number>` | none | Pull request to anchor to when running outside a session and outside CI. |
| `--json` | off | Machine-readable output. |

```bash theme={"system"}
outerlayer emit artifact shot.png --caption "The login page shows the error banner" --for LOGIN-01
```

More on `--test`:

* `--test` needs `--for` and an `.xml` file. Without either, the command exits 1.
* A name several tests share binds all of them, with a warning.
* `<name>=<path>:<line>` gives the test's location when the runner's file does not carry it, as in `--test "renders the banner=src/login.test.ts:42"`.
* The command exits 1 on a name the file does not hold, or on a location that is absolute or leaves the repository.

The kind comes from the file type: see the table in [Prove the work](/attach-evidence). Files cap at 8 MiB. A `.xml` file whose root is `<testsuites>` or `<testsuite>` is a `test` artifact. With `--for`, the command reads the XML first and exits 1 on a malformed file before sending anything. Without `--for`, the CLI sends an `.xml` file without checking it. The command prints the artifact's id, for a later `--replaces`.

Inside a recorded session, the artifact is queued and uploads on the next `outerlayer sync`, which prints its dashboard link. A session not launched with `OUTERLAYER_WORK` is refused. Outside a session, it uploads at once, anchored to `--pr`, CI's pull request, or the current git checkout. The command prints the link on the line after the id. `--json` carries it as `url`. With nothing to attach it to, it is refused.

A `--replaces` target already uploaded is hidden everywhere. One target still in the local queue is cancelled, so it never uploads. If you name several ids and one is still queued, the command is refused: run `outerlayer sync` first, then retry.

Exit codes: 0 recorded or queued, 1 refused or invalid.

See also: [Prove the work](/attach-evidence).

## outerlayer emit finding

Record one problem you hit in the factory.

```bash theme={"system"}
outerlayer emit finding --id <id> --category <category> --title <text> --file <path> --where <label>
                        [--line <number>] [--item <number>]
                        [--rule-path <path> --rule-relation <relation> [--rule-quote <text>] [--rule-line <number>]]
                        [--cause <text>] [--detail <text>] [--suggestion <text>]
                        [--command <text>] [--error <text>] [--test <name>] [--symbol <name>]
                        [--started-at <event>] [--ended-at <event>] [--untimed] [--find <text>]
                        [--problem <id> ... --link-reason <text> | --new]
                        [--trace-id <id>] [--turn <number>] [--round <number>]
                        [--head-sha <sha>] [--context-source-sha <sha>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--id <id>` | required | Your own key, 1 to 64 characters of `A-Z a-z 0-9 . _ -`. Recording the same id on the item again replaces it. |
| `--category <category>` | required | `context`, `flaky-test`, `tooling`, `environment`, `defect` or `other`. |
| `--title <text>` | required | One sentence. At most 200 characters. |
| `--file <path>` | required | The file the problem is in, or the instruction's own path for a `context` finding. |
| `--where <label>` | required | Where this was recorded, such as `"build session"`. At most 200 characters. |
| `--line <number>` | none | Line number in `--file`. |
| `--rule-path <path>` | none | The instruction a `context` finding is about. Required for `context`. |
| `--rule-relation <relation>` | none | How the instruction fails: `wrong`, `broken` or `missing`. Required for `context`. |
| `--rule-quote <text>` | none | The instruction's sentence, quoted. Required unless the relation is `missing`. At most 500 characters. |
| `--rule-line <number>` | none | Line number in `--rule-path`. |
| `--cause <text>` | none | What is wrong, or `unknown`. |
| `--detail <text>` | none | What happened, a few paragraphs. |
| `--suggestion <text>` | none | What would help. |
| `--command <text>` | none | The command that misbehaved. Used to join the finding to a known problem. |
| `--error <text>` | none | The first error line. Numbers, hashes, paths, versions and durations in it are ignored when problems are matched. |
| `--test <name>` | none | The test name, for a flaky test. |
| `--symbol <name>` | none | The function the problem is in, for a defect. |
| `--started-at <event>` | the first tool output that contains `--error` | The id of the session event where you first hit the problem. |
| `--ended-at <event>` | the newest event | The id of the session event where you got past it. |
| `--untimed` | off | Record the finding with no time. Only for a problem that cost none in this session. Cannot be combined with `--started-at` or `--ended-at`. |
| `--find <text>` | none | List the events of the session and its subagents that contain the text, and record nothing. |
| `--problem <id>` | none | A problem this finding reports. Repeat for each one, up to 5. Needs `--link-reason`. |
| `--link-reason <text>` | none | Why the finding reports each `--problem`. |
| `--new` | off | Record a new problem. Cannot be combined with `--problem`. |
| `--trace-id <id>` | none | The session's trace id, when there is one. |
| `--turn <number>` | none | The turn within the session where you hit it. |
| `--round <number>` | none | The round number, when your process has rounds. |
| `--head-sha <sha>` | none | The commit you were working on. |
| `--context-source-sha <sha>` | the recorded session's instructions commit | The commit of the instructions you were working from. |
| `--item <number>` | the recorded session's item | The work item. Required outside a recorded session. |
| `--json` | off | Machine-readable output. |

```bash theme={"system"}
outerlayer emit finding --id gate-skip --category tooling \
  --title "The pre-push gate prints green when a step was skipped" \
  --file scripts/git/pre-push-checks.mjs --where "build session"
```

A refused finding names the field to fix. The old flags `--subject`, `--kind`, `--severity`, `--verdict`, `--fixed` and `--source` are refused by name. Use `--category` instead.

When the finding matches a problem by key, it joins that problem, and the output names it. Otherwise it stores nothing, prints the closest problems, and exits 3. Run it again with `--problem` and `--link-reason`, or `--new`. See [Choose among candidates](/findings#choose-among-candidates).

When the session has events and no start is found, it stores nothing, prints the recent events with their ids, and exits 1. See [Mark when the problem started and ended](/findings#mark-when-the-problem-started-and-ended).

Three things are refused before anything is stored:

* a problem that is not in this factory;
* `--problem` without `--link-reason`;
* an event id that is not in the transcripts.

Exit codes: 0 recorded, 1 refused or invalid, 3 not stored: choose among the printed problems and run it again.

See also: [Findings](/findings).

## outerlayer emit findings

Record a batch of findings from a JSON file.

```bash theme={"system"}
outerlayer emit findings <file> [--item <number>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--item <number>` | the recorded session's item | The work item. Required outside a recorded session. |
| `--json` | off | Machine-readable output. |

```bash theme={"system"}
outerlayer emit findings review.json --item 412
```

The file holds `"schemaVersion": 3`, optional `headSha` and `contextSourceSha`, and 1 to 500 `findings` with the same fields and rules as `emit finding`. A finding may carry `problems` with `linkReason`, or `new: true`. Its `startedAt` and `endedAt` may be event ids as `emit finding` lists them, and default the same way. A finding with no start found stops the whole batch, unless it carries `"untimed": true`. `-` reads standard input. The whole batch is checked before anything is sent. A `schemaVersion` 1 or 2 file is refused: set `schemaVersion: 3`.

The findings that match a key are stored. The ones that need a choice are listed and not stored. Add `problems` with `linkReason`, or `new: true`, to those findings and run the command again. Findings already stored keep their problems. See [Choose among candidates](/findings#choose-among-candidates).

Exit codes: 0 recorded, 1 refused or invalid, 3 at least one finding was not stored and needs a choice.

See also: [Findings](/findings).

## outerlayer emit step

Report that you started a step of the work. The work item's Activity tab shows each step as it is reported.

```bash theme={"system"}
outerlayer emit step <name> [--item <number>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--item <number>` | the recorded session's item | The work item, by its number on the factory. |
| `--json` | off | Machine-readable output. |

```bash theme={"system"}
outerlayer emit step "Review round 2"
outerlayer emit step "Plan" --item 412
```

The name is free text of 1 to 80 characters on one line. OuterLayer ships no step names and checks none against a list. A step has a start and nothing else, so run the command again when the next step starts. The start time is the time you ran the command.

How it finds the item:

* With `--item`, it uses that item.
* Without it, inside a recorded session, it uses the item that session was launched for with `OUTERLAYER_WORK`.
* With neither, it exits 1, names `--item` and `OUTERLAYER_WORK`, and sends nothing.
* Inside a recorded session that was not launched on a work item, it is refused, as the other emit commands are.

Who started the step comes from your credential, never from what you send. A host build records the runner's name. Any other session records the display name of the member the key is bound to.

If the gateway cannot be reached, or answers with a 5xx, the step is queued in `~/.outerlayer/spool/steps.jsonl`. The queued step keeps the time you ran the command, and the command exits 0. A failed report never stops the agent. `outerlayer sync` sends each queued step once, and the step then shows at the time it started. A queued step older than 14 days is dropped. If the gateway refuses the step with a 4xx, the command prints the gateway's reason, exits 1 and queues nothing.

Exit codes: 0 reported or queued, 1 refused or invalid.

See also: [Review and merge](/review-the-work).

## outerlayer emit criteria

Record a work item's acceptance criteria as one list. The Criteria tab, its count and the item's status read this list. Nothing reads criteria out of the issue.

```bash theme={"system"}
outerlayer emit criteria <file> [--item <number>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--item <number>` | the recorded session's item | The work item, by its number on the factory. Required outside a recorded session. |
| `--json` | off | Machine-readable output. |

```bash theme={"system"}
outerlayer emit criteria criteria.json --item 412
```

The file is JSON, or an acceptance file ending in `.md`. `-` reads JSON from standard input.

```json theme={"system"}
{
  "criteria": [
    { "id": "AC-724-01", "text": "Given a signed-in user, when they log out, then the session ends.", "proof": null },
    { "id": "login.redirect", "text": "Login redirects to the page the user asked for.", "proof": "screenshot" }
  ]
}
```

| Field | Meaning |
| - | - |
| `id` | Your own id, 1 to 64 characters from `A-Z a-z 0-9 . _ : -`. It is the id `outerlayer emit artifact --for` and `outerlayer work comment --criterion` take. No two criteria may share one. |
| `text` | The criterion as your team wrote it. 1 to 2,000 characters. |
| `proof` | The artifact kind that proves it: `video`, `screenshot`, `report`, `log`, `test` or `file`. `test` is met by bound JUnit results in which every bound test passed. `null` means any artifact bound to the id proves it. |

An acceptance file is read as follows:

* Each numbered list item that opens with an id in backticks, such as `` `AC-412-01` ``, is one criterion.
* Its text is the rest of the item.
* A `(proof: <kind>)` written outside backticks names its proof.
* A bulleted item that names an id is a note about that criterion, so it is skipped.

```bash theme={"system"}
outerlayer emit criteria acceptance/412-login.md --item 412
```

The file may also carry a top-level `"drafted": true`. The build sets it on a list it drafted from the issue's description, because the issue held no criteria. Only a list an agent session or a factory key records keeps the mark. A list a person records is never marked drafted, even when its file says so. The item's Criteria tab says when its current list was drafted.

The list holds 1 to 200 criteria. The command checks the file before it sends anything, and names the id or value it refuses.

Every list you record is kept, with who recorded it and when. The newest is the item's current list.

**Only a person can replace a recorded list**, unless the repository's policy sets [`criteria.replace: anyone`](/policy-and-validators#the-policy-file). A drafted list is no exception: until a person replaces it, a later build's record is refused. Run the replacement with your own login, outside the session.

Recording a list asks for a new evaluation of the item's linked pull requests. An item with no linked pull request gets none, but its Criteria tab shows the list at once. An item with no recorded list shows no criteria.

Exit codes: 0 recorded, 1 refused or invalid.

See also: [Review and merge](/review-the-work), [Prove the work](/attach-evidence).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.