<!-- SPDX-License-Identifier: Apache-2.0
     https://www.apache.org/licenses/LICENSE-2.0 -->

# Runtime recipes — build, run, capture, hygiene

Companion to [`SKILL.md`](SKILL.md). Procedural detail for Steps 5–6
and Step 11: building the project distribution, running the adapted
reproducer with bounded resources, capturing streams, handling
dependency-resolving runtimes, and keeping the working tree clean
between issues.

## Build prerequisite

Some projects require a fresh build of `<default-branch>` before
the runtime exercises current behaviour. The project's recipe is
in [`<project-config>/runtime-invocation.md`](../../projects/_template/runtime-invocation.md)
under the *Build prerequisite* heading.

Examples (each project specifies its own):

- JVM-language projects with Gradle: `./gradlew :installDist`
- Python projects with build artefacts: `pip install -e .`
- Native-compiled projects: `make` or `cargo build --release`

If the recipe is empty (the runtime is on `PATH` out-of-the-box),
skip this step. The user may also pass `--no-build` to skip when
the runtime is current for the session.

After the build, confirm the runtime is invocable
(`<runtime> --version` or equivalent). If not, stop and surface
the build output for the user to inspect.

## Running with bounded resources

Invoke `<runtime>` on the adapted reproducer file per
[`<project-config>/runtime-invocation.md`](../../projects/_template/runtime-invocation.md)'s
*Run a single file* recipe.

| Setting | Default | Notes |
|---|---|---|
| Timeout | 60s | Override with `--timeout <seconds>`. Record any bump in `verdict.json.notes` so the verdict's runtime context is clear. |
| Working directory | `<scratch>` | The scratch directory per `<project-config>/reproducer-conventions.md`. Never the `<upstream>` checkout itself — the run may produce files. |
| Network | available | Many runtimes resolve dependencies on first use; see *Network and dependency handling* below. |
| JDK / interpreter version | the operator's default | For verdicts where it matters (`passes`, `fixed-on-master`), retry on the reporter's claimed version if reasonably available locally. |
| Streams | stdout + stderr captured | See *Capturing both streams* below. |

The capture command is built from the project's recipe. As an
illustrative shape:

```bash
timeout <timeout>s \
  <runtime-command> <scratch>/reproducer.<ext> \
  > <scratch>/stdout.log \
  2> <scratch>/stderr.log
EXIT=$?
```

Concatenate the captured streams into `<scratch>/run.log` with the
exact command on the first line plus the runtime version, started
and ended timestamps, and the exit code.

## Capturing both streams

Many reproducers print the bug indicator (stack traces, MOP errors,
*"expected X got Y"*) to **stderr**, not stdout. Capturing only
stdout silently loses the most important evidence.

Always capture both streams. Save them separately
(`<scratch>/stdout.log`, `<scratch>/stderr.log`) AND concatenated
into the human-readable `<scratch>/run.log`:

```text
$ <command>
<rev>          <short-sha-of-default-branch>
<runtime>      <runtime-version-string>
<started>      <ISO-8601>
<ended>        <ISO-8601>
<exit-code>    <int>
--- stdout ---
<stdout content>
--- stderr ---
<stderr content>
```

This shape is what [`issue-reassess`](../issue-reassess/SKILL.md)
expects to find when aggregating across a campaign.

## Network and dependency handling

Some runtimes resolve dependencies at run time (Grape for JVM
scripting languages, ad-hoc imports for Python in some setups).
For these, the resolution **must succeed** before the body's
behaviour is meaningful.

The protocol:

1. Run as normal, capturing both streams.
2. **Before** classifying the verdict, scan both streams for
   resolution errors. Patterns to look for (project-dependent;
   declared in `<project-config>/runtime-invocation.md`):
   - `ResolveException`, `unable to download`,
     `dependency not found`, `404` on a known repo URL, etc.
3. If a resolution error is present, classify
   `cannot-run-dependency` regardless of the exit code. A
   `0` exit code with a swallowed resolution error is a common
   trap — the run "completed" but the dependency-using code never
   executed.
4. If resolution succeeded, classify normally per
   [`verification.md`](verification.md).

For campaign sweeps that run many dependency-resolving reproducers,
consider isolating the dependency cache per campaign. The
`cache_isolation_flag` field in
[`<project-config>/runtime-invocation.md`](../../projects/_template/runtime-invocation.md)
declares the flag the skill passes through (e.g.,
`-Dgrape.root=<scratch>/grape` for JVM scripting languages with
Grape). When set, this keeps the operator's everyday cache from
being polluted by old reproducer dependencies.

## Working-tree hygiene

The `<upstream>` checkout is shared across reproducer runs in a
campaign. Files written by issue A's run that issue B picks up
corrupt B's verdict in ways that are hard to spot. Always reset
between issues.

Sources of cross-issue leak:

1. **Shape B test placement** — when a reproducer was adapted as
   a test under the project's source tree, the test file persists
   after the run. Reset it.
2. **Scratch files written into the source tree** — sometimes a
   reproducer writes a file with a path relative to its working
   directory; if the working directory was the `<upstream>`
   checkout, the file lives in the source tree now. The recipe
   above says *"Working directory: `<scratch>`"* precisely to
   avoid this; but reproducers using absolute paths can still
   leak.
3. **Compiled artefacts** — `.class`, `.pyc`, build caches in the
   source tree. Usually `.gitignore`d but can still influence
   later runs.

Reset protocol (sequential per issue, run at Step 11):

```bash
# In <upstream> checkout:
git stash --include-untracked || true   # save anything legit
git clean -fd                            # remove untracked files
git checkout -- .                        # revert tracked-file mods
```

A campaign may prefer a dedicated *throwaway* branch for runs, so
the reset can be `git reset --hard <campaign-base>` without
worrying about accidental loss of legitimate work.

If the reset itself fails, stop and surface — do not proceed to
the next issue with a known-dirty tree.

> **Source control.** The `git stash` / `git clean` / `git checkout`
> / `git reset` calls above are the **Git binding** of the framework's
> source-control capability
> ([`tools/github/source-control.md`](../../tools/github/source-control.md)),
> resetting the project's `<upstream>` working copy between runs. If
> the project's manifest enables a non-Git VCS under *Tools enabled →
> Source control*, substitute that tool's working-copy-reset binding
> (`hg revert --all` + purge, `svn revert -R` + cleanup, …) for the
> same abstract operation; the reset protocol is unchanged.

## JDK / interpreter version awareness

Reproducer outcomes can depend on the runtime version. The skill
records the version in `verdict.json.jdk` (or the runtime-specific
equivalent), and where the verdict matters:

- For `passes` / `fixed-on-master` verdicts on issues filed
  against an older JDK / interpreter version: if the older version
  is reasonably available locally (Gradle toolchains, `pyenv`,
  etc.), **retry** on the originally-affected version. A *"passes
  on JDK 21 master"* claim is weaker than *"passes on JDK 8 master,
  which the reporter used"*.
- For `still-fails-same` verdicts: less critical — the bug
  reproduces, the version is recorded as context.

## Cross-references

- [`SKILL.md`](SKILL.md) — orchestration; this file expands
  Steps 5, 6, and 11.
- [`extraction.md`](extraction.md) — how the reproducer file got
  produced.
- [`verification.md`](verification.md) — how the run output gets
  classified.
- [`<project-config>/runtime-invocation.md`](../../projects/_template/runtime-invocation.md) —
  project's build + run + cache-isolation recipe.
- [`<project-config>/reproducer-conventions.md`](../../projects/_template/reproducer-conventions.md) —
  scratch directory layout.
