CLAUDE.md@crates/orbit-engine · git:20260829.e25929a · 2026-08-29 · sha256 89274317f1d42333

CLAUDE.md@crates/orbit-engine git:20260829.e25929aA

Immutable. This exact content is served forever at /api/v1/blob/89274317f1d42333.

# orbit-engine

Project instructions for the activity/job execution engine.

## One job

Run a v2 activity or job to completion: resolve inputs through templates,
dispatch to a provider CLI or a deterministic action, record step results and
audit rows, and handle retry, resume, fan-out, and concurrency. It sits above
`orbit-agent`, `orbit-exec`, `orbit-store`, and `orbit-tools`, and is consumed
by `orbit-core`.

It owns *how* work runs. It does not own *what* work exists or who may ask for
it: catalog placement, workspace resolution, authorization, and task lifecycle
policy are Core's. The engine must never depend on `orbit-core`, `orbit-cmd`,
or any transport crate.

## `RuntimeHost` is the capability boundary

[`context::hosts`](src/context/hosts.rs) defines the single trait through which
job execution reaches anything the engine does not own — task reads and
updates, event emission, invocation queries, agent dispatch. Core implements it
in its `adapter/engine_host` module.

When execution needs a new capability, **add a method to `RuntimeHost`** with a
default that returns the `unsupported ... capability` error, and implement it in
Core. Do not smuggle the capability in by widening a dependency, threading an
`OrbitRuntime`-shaped handle through, or reading state directly from
`orbit-store` where a host method belongs.

The one deliberate exception to "engine stays provider-agnostic" is
[`activity_job::cli_runner`](src/activity_job/cli_runner), which names
`orbit_agent::{Agent, AgentConfig}` directly. That edge exists so `orbit-core`
stays clean of `orbit-agent` types; keep it inside `cli_runner` rather than
letting agent types spread across the engine.

## Internal layout

- [`activity_job/`](src/activity_job) — the run path: asset loading and
  catalogs, crew resolution, the dispatcher, `cli_runner` (argv, envelope,
  spawn, supervisor, orchestrator), `job_executor` (step, target, loop, fan-out,
  parallel, recovery, templating, validate), and the audit sinks.
- [`executor/automation/`](src/executor/automation) — the deterministic actions
  a job step can invoke without an agent. The `vcs` subtree (commit, push, PR,
  worktree, freshness, handoff) is the largest; it is organized by *operation*,
  and each new operation gets a file, not another arm in an existing one.
- [`context/`](src/context) — split by concern (`hosts`, `outcome`, `env`) and
  re-exported so `crate::context::X` paths stay stable.
- [`template.rs`](src/template.rs), [`condition.rs`](src/condition.rs) — pure
  rendering and predicate evaluation, no I/O.

The run path is where "several phases in one function" pressure shows up first.
The `job_executor` and `cli_runner` splits are the reference for relieving it:
name the phase, give it a file, keep the top-level flow readable.

## Crate-specific invariants

- **Boundary errors are translated here.** `DispatchError` and `CatalogError`
  are registered in `scripts/check-error-translation.sh`; their
  `dispatch_error_to_orbit` / `catalog_error_to_orbit` translators must stay in
  this crate ([`error_translation.md`](../../docs/design-patterns/error_translation.md)).
  A caller crate that maps their variants to `OrbitError` fails CI.
- **Redaction is not local.** `scripts/check-artifact-redaction-guardrail.sh`
  forbids `fn redact_*` in
  [`cli_runner/orchestrator.rs`](src/activity_job/cli_runner/orchestrator.rs)
  and [`cli_runner/argv.rs`](src/activity_job/cli_runner/argv.rs); argv and
  output redaction flows through `orbit_common::security::redaction`.
- **Child environments are composed, never inherited.** Spawn paths build the
  environment with `orbit_common::security::child_env` over a cleared
  environment, plus the shared provenance variables in
  [`context/env.rs`](src/context/env.rs).
- **Resume must stay correct.** Step results are durable; a change to
  `job_executor` step accounting needs coverage in the resume/recovery tests,
  not just the happy path.

## Tests

Sibling `tests/` directories for unit coverage
([`test_layout.md`](../../docs/design-patterns/test_layout.md)); crate-root
[`tests/`](tests) holds the end-to-end v2 runtime, CLI-agent, and
name-resolution integration tests; [`examples/`](examples) holds the runnable
v2 smoke programs. Put a new cross-module behavior in the crate-root
integration tests only when it genuinely exercises the public surface
end-to-end.