# MoonBit Coverage: CI Integration and CLI Reference

This document describes the behavior of `moonbitlang/moon_cove@0.3.3`.

## 1. Architecture and data model

MoonBit coverage consists of compiler instrumentation, runtime counters, test
execution, and report generation.

| Component | Responsibility |
| --- | --- |
| `moonc` | Inserts counters at source AST branch entries, function bodies, and selected expressions; emits `.trace.source` metadata. |
| `moonbitlang/core/coverage` | Accumulates probe hit counts and emits counter data between coverage delimiters. |
| `moon test --enable-coverage` | Compiles and executes instrumented tests, collecting `moonbit_coverage_*.txt` runtime logs. |
| `moon_cove` | Combines metadata and runtime logs, reads source files, and generates or uploads reports. |
| Coveralls / Codecov | Stores uploaded reports, associates them with commits and pull requests, and calculates changes between builds. |

Report generation requires source files, `.trace.source` metadata, and runtime
logs from a consistent source revision and instrumentation configuration.
Version `0.3.3` supports the `MOONCOVE00000004` metadata schema.

Each probe records a cumulative execution count. The data does not preserve
execution order or control-flow edge sequences. Different execution paths can
therefore produce identical reports. The metadata field `parent` describes
source-range nesting.

## 2. Activation and target selection

Instrumentation, reporter selection, and upload configuration are independent.

| Operation | Configuration | Result |
| --- | --- | --- |
| Instrument tests | `moon test --enable-coverage` | The compiler inserts probes, and the test driver collects runtime data. |
| Run tests and report uncovered code | `moon coverage analyze` | Existing runtime logs are removed, instrumentation is enabled, tests run, and a `simp_caret` report is generated by default. |
| Select the MoonBit reporter for `moon coverage` | `MOON_COVE_REPORT_ENABLED=1` | The toolchain's `moon_cove.wasm` runs through `moonrun`. |
| Generate a local Coveralls report | `-f coveralls` | JSON is written locally, without automatic CI metadata or token detection. |
| Upload to Coveralls | `-f coveralls --send-to coveralls` | CI detection and upload metadata resolution run; Git metadata is included when the applicable policy enables it. |

Running in CI does not initiate an upload automatically.

### Target terminology

| Target | Example | Scope |
| --- | --- | --- |
| Test compilation target | `moon test --target native --enable-coverage` | Selects the backend for the code under test and determines which conditionally compiled code participates. |
| Reporter execution target | `moonx --target wasm moonbitlang/moon_cove@0.3.3` | Selects how the reporter itself executes. |
| Upload destination, represented by `SendTarget` | `--send-to coveralls` or `--send-to codecov` | Selects the upload service, token source, and automatic Git metadata policy. |

The Wasm reporter can process compatible coverage data produced by tests running
on `native`, `wasm`, `wasm-gc`, and `js`. The test and reporter execution targets
do not need to match. The `moon_cove` CLI itself has no `--target` option.

When the test target is omitted, `moon` follows the project configuration and its
version-specific default. Specify the test target explicitly in CI. The verified
`moonx` version defaults to `wasm` and marks its native execution option as
deprecated; the examples below select `wasm` explicitly.

## 3. Report generation

Run the following commands from the root of the project under test:

```sh
moon test --target wasm --enable-coverage
moonx --target wasm moonbitlang/moon_cove@0.3.3 -f summary
moonx --target wasm moonbitlang/moon_cove@0.3.3 -f html -o _coverage
```

The explicit package version selects a reporter independently of the version
bundled with the toolchain. Using `@latest` refreshes the registry index and
resolves the latest release. Pin a package version when reproducibility is
required.

The root executable package can also be installed:

```sh
moon install moonbitlang/moon_cove@0.3.3
moon_cove --version
moon_cove -f summary
```

The current executable package is `moonbitlang/moon_cove`, and the installed
command is `moon_cove`. Earlier releases used the `cmd/moon_cove_report`
subpackage. Package coordinates must match the selected release.

### Toolchain reporter selection

```sh
MOON_COVE_REPORT_ENABLED=1 moon coverage report -f summary
MOON_COVE_REPORT_ENABLED=1 moon coverage analyze -- -f caret
```

The verified `moon coverage` implementation selects its reporter as follows:

| Value of `MOON_COVE_REPORT_ENABLED` | Selected reporter |
| --- | --- |
| `1`, or a case-insensitive spelling of `true` | The toolchain's `bin/moon_cove.wasm`, executed by `moonrun`. |
| Unset, `0`, `false`, or any other value | The toolchain's OCaml `moon_cove_report`. |

```sh
moon version --all
moonx --target wasm moonbitlang/moon_cove@0.3.3 --version
moonrun "$HOME/.moon/bin/moon_cove.wasm" -- --version
MOON_COVE_REPORT_ENABLED=1 moon coverage report --dry-run -f summary
```

The third command assumes the default toolchain installation directory. Adjust
the path for a custom installation. The fourth command prints the reporter
invocation without executing it.

## 4. `moon coverage` commands

| Command | Behavior |
| --- | --- |
| `moon coverage report ...` | Generates a report from existing artifacts without running tests. Report arguments are forwarded to the selected reporter. |
| `moon coverage analyze ...` | Removes existing runtime logs, runs instrumented tests, and generates a report. |
| `moon coverage clean` | Removes runtime logs matching `moonbit_coverage_*.txt` from the build directory. Source files and `.trace.source` metadata are retained. |

The `analyze` command distinguishes test arguments from reporter arguments:

| Argument | Behavior |
| --- | --- |
| `-p, --package <package>` | Filters the final report by package. In the verified implementation, this does not restrict which tests run. |
| `-t, --test-flag <argument>` | Forwards one argument to `moon test`; may be repeated. This compatibility option is hidden from the current help output. |
| `-- <reporter arguments>` | Forwards the remaining arguments to the reporter. |

For example:

```sh
MOON_COVE_REPORT_ENABLED=1 moon coverage analyze \
  --test-flag=--target=wasm -- -f summary
```

`analyze` prepends `-f=simp_caret` before appending the arguments following `--`.
The reporter uses the last format option, so this command produces a summary.

Use separate test and report commands to control the test selection and backend
explicitly:

```sh
moon coverage clean
moon test path/to/package --target native --enable-coverage
moonx --target wasm moonbitlang/moon_cove@0.3.3 -p path/to/package -f summary
```

`moon coverage report` starts the reporter in the project's resolved source
directory. Direct invocations through `moon_cove` or `moonx` use the current
working directory. For projects with a configured `source` subdirectory, verify
that artifact discovery includes the intended build output.

## 5. Reporter CLI reference

The following options apply to `moon_cove@0.3.3`. With `moonx`, place them after
the package coordinate. With `moon coverage report`, place them after `report`.

### Input and output options

| Argument | Default and behavior |
| --- | --- |
| `[trace-sources...]` | Explicit `.trace.source` files. If omitted, the current directory is searched recursively. |
| `-t, --runtime-log <path>` | An explicit runtime log; may be repeated. If omitted, the current directory is searched recursively for filenames beginning with `moonbit_coverage_`. |
| `-f, --format <format>` | Defaults to `bisect`. Format names are case-insensitive. The last occurrence takes precedence. |
| `-o, --output <path>` | Output file or HTML directory; defaults are listed below. Text formats write to stdout and ignore this option. |
| `-p, --package <package>` | Matches a package name exactly or by `/suffix`. Glob patterns are not supported. |
| `-F, --file <file>` | Matches a source filename exactly or by `/suffix`. Glob patterns are not supported. |
| `--source-paths <roots>` | Comma-separated source lookup directories; defaults to `.`. These are not coverage artifact search directories. |
| `--absolute-file-paths` | Retains resolved absolute paths. By default, files within the current directory are reported with relative paths. |
| `--ignore-missing-files` | Enables missing-source skipping during report rendering; enabled by default. |
| `--no-ignore-missing-files` | Disables missing-source skipping during report rendering. |
| `--verbose` | Writes collection diagnostics, including the source file count, to stderr. |
| `-h, --help` | Displays command help. |
| `-V, --version` | Displays the reporter version. |

The collection stage must read source files to calculate byte offsets. A missing
source file at that stage causes an error even when `--ignore-missing-files` is
enabled. The option does not make source files optional throughout the pipeline.

To select artifacts explicitly, substitute the paths generated by the project:

```sh
moon_cove path/to/package.trace.source \
  -t path/to/moonbit_coverage_1.txt \
  -t path/to/moonbit_coverage_2.txt \
  --source-paths src,generated \
  -F example.mbt -f caret
```

### Report formats

| Format | Content | Default output |
| --- | --- | --- |
| `summary` | Files with incomplete probe coverage, followed by aggregate probe counts. | stdout |
| `full_summary` | All files, followed by aggregate probe counts. | stdout |
| `sorted_summary` | Selected coverage points ranked by hit count, with source context. The CLI displays at most 50 entries. | stdout |
| `caret` | Uncovered lines with source context and caret markers; does not parse MoonBit syntax. | stdout |
| `simp_caret` | Uncovered locations grouped by parsed top-level declaration. | stdout |
| `html` | Offline HTML report pages. | `_coverage/` |
| `coveralls` | Line-oriented coverage in Coveralls JSON format. | `coveralls.json` |
| `cobertura` | Cobertura XML. | `cobertura.xml` |
| `bisect` | `BISECT-COVERAGE-4` data. | `bisect.coverage` |

Redirect stdout to save a text report:

```sh
moon_cove -f full_summary > coverage-summary.txt
```

Summary counts use instrumentation probes as the denominator. Coveralls uses
source lines with coverage counts, so the resulting percentages may differ.
In line-oriented data, `null` denotes a line without an independent count, `0`
denotes an unhit instrumented line, and a positive value denotes a hit count.
Multiple probes on one line are reduced by the reporter's aggregation rule;
the result is not an exact execution count for every statement on that line.

Successful report generation does not enforce a coverage threshold. The CLI has
no `--fail-under` option and exits successfully when a report is generated even
if uncovered code remains.

### Upload options

Upload options are evaluated in the `coveralls` format path. Specifying
`--send-to coveralls` alone does not change the default format from `bisect`.

| Argument | Behavior |
| --- | --- |
| `--send-to coveralls\|codecov` | Enables upload and CI metadata resolution. Without this option, the report is written locally. |
| `--service-name <name>` | Explicit CI service name; may be inferred during upload. |
| `--service-job-id <id>` | Explicit job identifier; may be read from the CI environment during upload. |
| `--service-pull-request <number>` | Explicit PR number; may be inferred during upload. |
| `--service-number <number>` | Build number. Automatic environment-based inference is not implemented. |
| `--coveralls-token <token>` | Explicit upload token. Environment variables are the usual credential source. |
| `--coveralls-include-git-info` | Includes Git SHA, author, committer, commit message, and branch metadata. |
| `--coveralls-parallel` | Writes `parallel: true` to the report. It does not send a parallel-build completion webhook. |

Uploads use a JSON file in a private temporary directory, which is removed after
the upload attempt. In this mode, `-o` does not retain a local report. To inspect
a report locally, run a separate command without `--send-to` and supply the
required metadata explicitly.

## 6. CI detection and upload policy

### Environment detection and metadata resolution

Automatic metadata resolution runs only with `-f coveralls --send-to ...`.
Local JSON generation does not invoke this logic, even when CI markers and token
variables are present in the environment.

| CI platform | Detection condition | `service_name` | Job ID source | PR number source |
| --- | --- | --- | --- | --- |
| CircleCI | `CIRCLECI=true` | `circleci` | `CIRCLE_BUILD_NUM` | `CIRCLE_PR_NUMBER`, otherwise the final path segment of `CIRCLE_PULL_REQUEST`. |
| Travis | `TRAVIS=true` | `travis-ci` | `TRAVIS_JOB_ID` | `TRAVIS_PULL_REQUEST`. |
| GitHub Actions | `GITHUB_ACTIONS=true` | `github` | `GITHUB_RUN_NUMBER` | `PULL_REQUEST_NUMBER`, otherwise the number parsed from `GITHUB_REF=refs/pull/<number>/...`. |
| GitLab CI | `GITLAB_CI=true` | `gitlab-ci` | `CI_JOB_ID` | `CI_MERGE_REQUEST_IID`. |

Detection requires an exact match with the lowercase string `true`. Conditions
are evaluated in table order. The generic marker `CI=true` does not identify a
platform. An inferred PR value of `false` is treated as an absent PR number.

Explicit nonblank options take precedence over inferred values. Leading and
trailing whitespace is removed. On an unrecognized CI platform, provide both
`--service-name` and `--service-job-id`; a missing job ID causes upload to fail.
Setting `--service-name github` does not itself enable GitHub Actions detection.

### Automatic Git metadata

Within the `coveralls` format path, Git metadata is included when:

```text
include_git = explicit --coveralls-include-git-info
              OR (destination is Coveralls
                  AND detected CI is GitHub Actions or CircleCI)
```

| Invocation | Behavior without an explicit Git option |
| --- | --- |
| GitHub Actions or CircleCI uploading to Coveralls | Includes Git metadata automatically. Version `0.3.3` restores this OCaml-compatible behavior. |
| Travis or GitLab CI uploading to Coveralls | Does not include Git metadata automatically; use the explicit option. |
| Any CI uploading to Codecov | This reporter does not include Git metadata automatically; use the explicit option. Metadata collection performed by `codecovcli` is separate. |
| Local Coveralls JSON generation | Does not include Git metadata automatically; use the explicit option. |

The Coveralls API specification lists `git` as a required field. Use the explicit option when
complete commit metadata is required outside the automatic cases.
See the [Coveralls API parameters](https://docs.coveralls.io/api-jobs-endpoint#parameters).

Git metadata is collected through local Git commands and requires readable
repository metadata for the source checkout. The branch is obtained from
`git rev-parse --abbrev-ref HEAD`; variables such as `GITHUB_HEAD_REF` are not
consulted. A detached checkout therefore produces `HEAD`. Coveralls may associate
the build with a named PR branch on the server, so the displayed branch need not
equal the value supplied by the reporter.

The Wasm reporter accesses environment variables and executes `git` and upload
commands through host interfaces provided by `moonrun`. Any configured runtime
policy must permit the required operations.

### Credentials, upload commands, and failures

| Destination | Default token source | Upload executable |
| --- | --- | --- |
| Coveralls | `COVERALLS_REPO_TOKEN`; included in the report JSON. | `curl` |
| Codecov | `CODECOV_TOKEN`; passed through the child process environment and omitted from the report JSON. | `codecovcli` |

The upload executables must be installed separately. The reporter does not
install them or implement automatic retries. A nonzero exit status from an
upload process causes the command to fail.

For Coveralls, the client permits an empty token only when Travis is detected.
This inherited policy does not guarantee that the service will accept an
unauthenticated request. Configure credentials according to the service's
requirements. For Codecov, the client permits an empty token; acceptance depends
on the repository and CI configuration at Codecov.

The reporter does not read `.coveralls.yml` or implement general support for
variables such as `COVERALLS_SERVICE_NAME` and `COVERALLS_GIT_BRANCH`. Configure
parallel-build numbering and completion notifications separately in CI.
See the [Coveralls parallel-build webhook](https://docs.coveralls.io/api-parallel-build-webhook).

## 7. GitHub Actions configuration

The following example assumes that the repository has enabled the Coveralls
GitHub integration and can authenticate uploads with `GITHUB_TOKEN`. Other
integration configurations may require a Coveralls repository token.

```yaml
name: Coverage

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Install MoonBit
        run: |
          curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
          echo "$HOME/.moon/bin" >> "$GITHUB_PATH"

      - name: Test with coverage
        run: moon test --target wasm --enable-coverage

      - name: Upload coverage
        run: |
          moonx --target wasm moonbitlang/moon_cove@0.3.3 \
            -f coveralls --send-to coveralls
        env:
          COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

The workflow explicitly enables instrumentation and pins the reporter version.
The reporter obtains the job ID and PR number from the GitHub Actions environment
and includes Git metadata under the policy described above. Direct invocation
through `moonx` does not require `MOON_COVE_REPORT_ENABLED`.

To use `MOON_COVE_REPORT_ENABLED=1 moon coverage report -f coveralls --send-to coveralls`
instead, ensure that the toolchain's bundled reporter contains the automatic Git
metadata fix introduced in `0.3.3`.

Source revision, compiler version, test target, and test selection all affect the
result. Pinning the reporter fixes only report-generation behavior. Reproducible
baselines also require a consistent test toolchain and target.

## 8. Artifact management and repeated runs

The reporter does not select only the most recent test run. It sums the selected
runtime counts for each file and probe.

- Retain multiple logs when intentionally combining test runs from the same source
  revision and instrumentation configuration.
- Use `moon coverage clean` before collecting a report for a single test run.
- After changing the source revision, compiler, or test target, existing metadata
  may also be incompatible. Use `moon clean` and regenerate artifacts when
  necessary; `moon clean` removes the entire build output directory.
- Keep incompatible target artifacts outside the same automatic discovery scope.
  The reporter has no target filter. Conflicting file metadata or probe counts
  cause errors.

`--target-dir` configures the build directory used by `moon`. The verified
`moon coverage report` implementation does not use it to constrain the reporter's
recursive search. If build artifacts are outside the source directory, specify
the `.trace.source` files and `-t` logs explicitly, or invoke the reporter from a
working directory that includes them.

## 9. Interpreting Coveralls comparisons

Coveralls compares uploaded build reports. An ordinary push build is compared
with the previous push report on the same branch, which may span multiple commits
without individual reports. PR builds use their associated base build. A missing
baseline report or incomplete SHA association can prevent that comparison.

The documented minimum configuration reports default-branch pushes and PR builds.
The recommended configuration reports pushes on all branches. When a PR has
diverged from its target branch, distinguish the merge commit actually tested
from the merge base used for comparison.
See [Coveralls build types and recommended configurations](https://docs.coveralls.io/build-types).

A file unchanged by the latest commit may still differ from the previous
reported revision. Check the two report SHAs, align the source lines, and then
compare counts. New declarations without probes can shift line numbers without
changing coverage. New unhit probes can change the coverage denominator.

## 10. Troubleshooting

| Symptom | Checks |
| --- | --- |
| No coverage source or runtime data | Confirm that instrumented tests ran and that artifact discovery includes their output. |
| Probe count mismatch or conflicting file metadata | Check for artifacts from different source revisions, targets, or compiler versions. |
| Source file not found | Verify the checkout and configure the appropriate source roots with `--source-paths`. |
| Parser diagnostics from `simp_caret` | This format depends on parser compatibility. Version `0.3.3` uses parser `0.3.19`, which may reject syntax such as legacy `try?`. Use `-f caret` to report without parsing source syntax. |
| CI metadata absent from local JSON | Local output does not perform automatic CI resolution. Supply metadata explicitly or inspect the payload generated by the upload path. |
| SHA or committer absent from Coveralls | Check the reporter version, destination, and Git inclusion policy. Versions `0.3.0` through `0.3.2` lack automatic Git metadata for GitHub Actions and CircleCI uploads. |
| No base build found for a PR | Check that the target branch has the required report and that SHA and PR metadata are complete. |
| No upload after specifying `--send-to` | Ensure that `-f coveralls` is also specified. |
| Wasm execution or Git/upload commands fail | Verify the `moonrun` version, required host executables, and runtime policy permissions. |