Skip to content

feat(cli): 2.D — relavium run wired to the @relavium/core engine#41

Merged
cemililik merged 8 commits into
mainfrom
development
Jun 22, 2026
Merged

feat(cli): 2.D — relavium run wired to the @relavium/core engine#41
cemililik merged 8 commits into
mainfrom
development

Conversation

@cemililik

@cemililik cemililik commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What & why

Phase-2 workstream 2.D — the M3 keystone: relavium run is wired to the Phase-1 @relavium/core engine. This is the first real engine consumer and the first consumption of the 2.B config loader and the @relavium/llm provider seam, so it doubles as the engine's integration harness.

relavium run <workflow> now: resolves the workflow (explicit path or an id/slug under .relavium/workflows/), coerces + validates --input against the declared inputs, builds the engine, drives RunHandle.events through a renderer, forwards SIGINT as a cooperative cancel, and maps the terminal event to a deterministic exit code (0 completed · 1 failed/cancelled · 2 invalid invocation · 3 gate-paused).

Refs: ADR-0036 (run-loop substrate), ADR-0038 (host-injected provider resolution), ADR-0022 (run UUID), ADR-0047 (CLI framework).

Commits

  • feat(llm): add defaultProviders() — a keyless adapter registry centralizing the provider→adapter mapping (incl. DeepSeek via the OpenAI-compatible adapter) in the seam package. Public-API addition to @relavium/llm (realizes ADR-0011/0038; no new decision). No vendor type crosses the seam.
  • feat(cli): the relavium run core + node-backed ExecutionHost, host-injected provider resolution, fail-closed ToolHost, a RunRenderer seam (minimal plain + NDJSON), and the engine wiring.
  • fix(cli): review + adversarial-verify follow-up (see below).
  • docs(roadmap): mark 2.A + 2.B Done (PR feat(cli): Phase 2 — 2.A CLI skeleton + 2.B config resolution (ADR-0047/0048) #40).

Scope decisions (deferred, tracked)

  • In-memory run store now → durable SQLite history is 2.H.
  • RELAVIUM_<PROVIDER>_API_KEY env keys now → OS keychain is 2.C.
  • Minimal plain + NDJSON renderers behind a seam → rich ink TUI is 2.E, the finalized --json envelope is 2.F.
  • Fail-closed ToolHost ({}) — every capability-backed built-in tool is cleanly unavailable (typed failure), never an insecure stub. Capability wiring (fs/process/egress) is deferred to a security-reviewed workstream (recorded in deferred-tasks.md).
  • human_gate exits 3 now; the interactive prompt + relavium gate resume are 2.G/2.H.

Review → adversarial-verify follow-up (folded into fix(cli))

Three independent reviews returned merge-ready, no blockers; a follow-up adversarial-verify pass caught two more issues. All resolved:

  • Provider-key pre-flight — a missing key is now a clean exit 2 + RELAVIUM_<PROVIDER>_API_KEY hint before start, not a swallowed mid-run exit 1. Scoped to each inline agent's primary provider only: auth ∉ RETRYABLE_KINDS, so a missing primary key is fatal at attempt 1 (no failover) → guaranteed-needed; a fallback_chain/$ref key is conditional and defers to runtime, so the pre-flight is a strict subset that never false-fails a valid run.
  • Cancellation/gate coverage — added human_gate→exit 3, a deterministic SIGINTrun:cancelled→exit 1 test (event-driven, no real-timer polling), and a 25-run SIGINT-listener-hygiene test for the 2.K harness; hardened the listener registration to be leak-free.
  • resolve.ts robustness — mirrors the 2.B statSync-first discipline: EACCES / non-regular-file / over-2-MiB is a clean exit 2, never silently "not found"; size cap enforced before read.
  • Input coercion--input n= (empty) now errors instead of silently coercing to 0; n=0 stays valid.

The verify pass independently re-confirmed: the seam holds (agent.provider ≡ ProviderId, no cast), no secret is logged/stored/rendered (key presence-checked then discarded), engine purity, no new runtime dependency, and the exit-code mappings — via mutation testing + worker probing.

Already-satisfied obligation (verified, not re-implemented)

The maintainer-decided "reject a secret-typed input in an agent prompt_template at parse" obligation assigned to 2.D was found already enforced by the 1.L2 taint analysis (collect.tsanalyzeSecretTaintWorkflowSecretLeakError), with tests for the direct, transitive-via-context, and $ref cases. 2.D made relavium run the first live consumer and confirmed it end-to-end. Marked resolved in deferred-tasks.md; no new code.

Toolchain

pnpm run ci green end-to-end: lint, typecheck (+ tools), 104 CLI tests (full monorepo green), build, format:check, the @relavium/llm seam fence, and engine-deps. Leakwatch clean on the new code (one pre-existing 2.B test false-positive, untouched here).

🤖 Generated with Claude Code

Summary by Sourcery

Wire the relavium run CLI command to the @relavium/core engine with minimal plain and NDJSON renderers, deterministic exit codes, and SIGINT-driven cancellation, and introduce a keyless provider registry in @relavium/llm alongside updated roadmap and reference docs.

New Features:

  • Implement the real relavium run command that resolves workflows by path or id, validates and coerces --input values, executes workflows via the core engine, and maps terminal outcomes to exit codes 0/1/2/3.
  • Add minimal human-readable and NDJSON run renderers behind a RunRenderer seam for lifecycle event streaming.
  • Introduce a CLI provider resolution seam that pre-flights required provider API keys via environment variables and injects keyless adapters from @relavium/llm into the engine.
  • Provide a node-backed ExecutionHost and fail-closed ToolHost wiring for the CLI engine, using an in-memory run store and checkpointer.

Enhancements:

  • Refine the CLI program construction to pass a runtime context into command registration, allowing real commands like run to set exit codes while keeping other commands as stubs.
  • Harden workflow resolution to treat non-regular files, unreadable workflows, and oversized workflow files as explicit invalid-invocation errors instead of silent misses.
  • Tighten global option resolution and exit-code handling in the top-level CLI run boundary so command outcomes drive the process exit code via a shared result holder.
  • Improve --input handling with stricter parsing and type coercion semantics that reject unknown inputs, invalid values, and missing required inputs.

Documentation:

  • Update CLI reference documentation for relavium run with details on input coercion, SIGINT cancellation, provider key pre-flight, gate-paused semantics, and current implementation status.
  • Refresh roadmap documents and README to mark Phase 2.A and 2.B as complete, describe 2.D progress, and record that secret-typed prompt interpolation is already enforced by existing taint analysis.
  • Record a new deferred task documenting the fail-closed CLI ToolHost and its planned security-reviewed capability wiring.

Tests:

  • Add extensive tests for the relavium run command covering happy-path execution, NDJSON output, failure mapping, gate pauses, SIGINT-driven cancellation, and SIGINT-listener hygiene.
  • Add tests for input parsing/coercion, workflow source resolution edge cases, provider pre-flight logic, and the NDJSON/plain renderers to ensure robustness and determinism.
  • Update existing CLI run tests to target a remaining stub command now that run is implemented.

Summary by CodeRabbit

Release Notes

  • New Features
    • Enhanced relavium run with repeatable --input k=v (duplicate/unknown/missing/invalid-value detection), plain output, and --json NDJSON rendering.
    • Added cooperative Ctrl-C cancellation, human-gate pause reporting, and deterministic exit codes for completed/paused/failed runs.
    • Implemented workflow source resolution (including extension fallback) with file-size guarding and stricter invocation validation, plus pre-flight API key checks for inline agents.
  • Documentation
    • Updated Phase 2 CLI status and expanded relavium run command reference for inputs, SIGINT, and human-gate behavior.
  • SDK/Library Updates
    • Re-exported defaultProviders and MAX_SOURCE_CHARS for external use.

cemililik and others added 4 commits June 22, 2026 07:31
PR #40 merged: build-phase-2 workstreams 2.A (CLI skeleton + process contract) and 2.B
(config resolution loader) are complete.

- phase-2-cli.md: Status → In progress; §2.A and §2.B headings → ✅ Done (PR #40).
- current.md: "What is active now" reflects 2.A/2.B landed + the next spine step (2.D → engine).
- README.md / CLAUDE.md: high-level status refreshed (Phase 2 underway / in progress), still
  pointing at current.md as the canonical live-status home.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose a single public factory that maps each ProviderId to its keyless
@relavium/llm adapter (DeepSeek via the OpenAI-compatible adapter with its
provider id). A host wires this into AgentRunnerDeps.resolveProvider and injects
the API key per call via keyFor, so the provider->adapter mapping lives in the
seam package rather than being re-derived in every surface (CLI, desktop,
VS Code). No vendor SDK type crosses the seam -- the return is
Record<ProviderId, LlmProvider>, and the seam fence stays airtight.

First consumed by the CLI's `relavium run` (workstream 2.D).

Refs: ADR-0011, ADR-0038

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The M3 keystone -- the first real consumer of the Phase-1 engine, and the first
consumption of the 2.B config loader and the @relavium/llm provider seam.

`relavium run <workflow>` now resolves the workflow (explicit path or an id/slug
under .relavium/workflows/), coerces and validates --input against the declared
inputs, builds the engine, drives its RunHandle.events stream through a renderer,
forwards SIGINT as a cooperative cancel, and maps the terminal event to a
deterministic exit code (0 completed / 1 failed|cancelled / 2 invalid invocation
/ 3 gate-paused).

Wiring (all injectable for tests + the 2.K harness):
- engine/host.ts: a real node-backed ExecutionHost (ISO wall clock, UUID ids per
  ADR-0022, setTimeout timers, AbortController) over the in-memory run store
  (durable history -> 2.H).
- engine/providers.ts: host-injected provider resolution (ADR-0038) over
  @relavium/llm defaultProviders(); keys read per-call from
  RELAVIUM_<PROVIDER>_API_KEY (OS keychain -> 2.C), never logged or stored.
- engine/build-engine.ts: the standard node executor + expression sandbox and a
  fail-closed ToolHost ({}) -- capability wiring (fs/process/egress) deferred to
  a security-reviewed workstream (deferred-tasks).
- render/renderer.ts: a RunRenderer seam with minimal plain + NDJSON renderers
  (rich ink TUI -> 2.E, finalized --json envelope -> 2.F).
- commands/run.ts is framework-free; commands/specs.ts registers the real action.

Tests: real-engine e2e (input->transform->output to run:completed exit 0; a
runtime sandbox error to run:failed exit 1), input coercion/validation, workflow
resolution, and both renderers.

Docs: commands.md gains a `relavium run` implementation-status note; the
secret-in-prompt_template parse-rejection obligation assigned to 2.D was verified
already-satisfied by the 1.L2 taint analysis (collect.ts/analyze.ts/parser.ts +
analyze.test.ts) -- marked resolved; the fail-closed ToolHost recorded as a
security follow-up in deferred-tasks.

Refs: ADR-0036, ADR-0038, ADR-0022, ADR-0047

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three independent reviews of the 2.D `relavium run` work returned merge-ready with
a few low/medium findings; an adversarial verify pass surfaced one more. This
resolves them all:

- Provider-key pre-flight (review L1): `relavium run` now pre-flights provider keys
  so a missing key is a clean exit 2 with the RELAVIUM_<PROVIDER>_API_KEY hint,
  rather than a swallowed mid-run failure (exit 1). Scoped to each referenced
  inline agent's PRIMARY provider only: `auth` is not in RETRYABLE_KINDS, so a
  missing primary key is fatal at the first attempt (the FallbackChain never fails
  over from it), making it guaranteed-needed; a fallback_chain or $ref agent's key
  is conditional and still surfaces at runtime, so the pre-flight is a strict
  subset that never false-fails a valid run. One resolver (reading the io.env seam)
  is shared by the pre-flight and the engine. The key value is only presence-checked
  -- never logged, stored, or rendered.
- Exit-3 + cancellation coverage (review M1): added an e2e human_gate -> exit 3
  test, a deterministic SIGINT -> run:cancelled -> exit 1 test (event-driven
  coordination + a generous ceiling, no real-timer polling), and a 25-run
  SIGINT-listener-hygiene test for the 2.K harness. Hardened run.ts so the SIGINT
  registration is adjacent to the try/finally that removes it (no leak window).
- resolve.ts robustness (review L2): mirror the 2.B config loader's statSync-first
  discipline -- an existing-but-unreadable file (EACCES), a non-regular file, or one
  over a 2 MiB cap is now a clean exit 2, never silently mis-reported as "not
  found"; the size cap is enforced before the file is read.
- Input coercion (review): reject an empty/whitespace `--input n=` for a number
  input instead of silently coercing it to 0; `n=0` stays valid.

Docs: commands.md `relavium run` notes updated for the key pre-flight and SIGINT.

Refs: ADR-0038

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Wires relavium run into the @relavium/core engine with a new CLI runtime context, engine/host/provider wiring, run-event renderers, and extensive tests around workflow resolution, input coercion, provider key pre-flight, SIGINT-driven cancellation, and exit code behavior; also introduces a default keyless provider registry in @relavium/llm and updates roadmap/docs to mark Phase-2 2.A/2.B as done and describe 2.D behavior.

Sequence diagram for relavium run wiring to the core engine

sequenceDiagram
  actor User
  participant CliRun as run
  participant RunCmd as runCommand
  participant ProvRes as createProviderResolver
  participant Engine as WorkflowEngine
  participant Renderer as RunRenderer

  User->>CliRun: run(argv, io)
  CliRun->>CliRun: extractGlobalOptions
  CliRun->>CliRun: resolveGlobalOptions
  CliRun->>CliRun: buildProgram(io, context)
  CliRun->>RunCmd: runCommand({workflow, input}, deps)

  RunCmd->>RunCmd: loadResolvedConfig
  RunCmd->>RunCmd: resolveWorkflowSource
  RunCmd->>RunCmd: parseWorkflow
  RunCmd->>RunCmd: parseInputArgs
  RunCmd->>RunCmd: resolveInputs

  RunCmd->>ProvRes: createProviderResolver
  RunCmd->>RunCmd: neededProviderIds
  loop primary providers
    RunCmd->>ProvRes: keyFor
  end

  RunCmd->>Engine: buildEngine({ providers })
  RunCmd->>Engine: start({ workflow, inputs })
  activate Engine

  RunCmd->>Renderer: createJsonRenderer / createPlainRenderer

  loop handle.events
    Engine-->>RunCmd: RunEvent
    RunCmd->>Renderer: onEvent(event)
    RunCmd->>RunCmd: nextOutcome
  end
  deactivate Engine

  User-->>CliRun: Ctrl-C (SIGINT)
  CliRun->>RunCmd: SIGINT handler
  RunCmd->>Engine: handle.cancel

  RunCmd-->>CliRun: ExitCode (0/1/2/3)
  CliRun-->>User: process exit code
Loading

File-Level Changes

Change Details Files
Introduce real relavium run command wired to the core engine with deterministic exit codes and SIGINT-driven cancellation.
  • Refactor command registration to take a CommandContext, making run a real command while leaving others as not-yet-implemented stubs.
  • Implement runCommand core that loads config, resolves workflow source, parses workflow, coerces/validates --input, pre-flights provider keys, builds the engine, consumes run events via a renderer, forwards SIGINT to handle.cancel, and maps terminal outcomes to exit codes 0/1/3.
  • Update top-level run boundary to resolve global options, build the program with context, and read command exit codes from a shared result object, including JSON-mode handling for commander parse errors.
  • Add extensive tests for runCommand and run covering success, failure, gate pause, invalid invocations, SIGINT behavior, and listener hygiene.
apps/cli/src/commands/specs.ts
apps/cli/src/commands/run.ts
apps/cli/src/run.ts
apps/cli/src/run.test.ts
apps/cli/src/commands/run.test.ts
Add workflow resolution, input coercion, provider resolution, and renderers as separable seams.
  • Implement workflow resolution helper that supports explicit paths and project .relavium/workflows ids, enforces a 2MiB size cap, and cleanly distinguishes not-found from unreadable/non-regular files.
  • Implement --input parsing and coercion against workflow-declared inputs, including validation for unknown keys, missing required inputs, and stricter number/boolean coercion.
  • Implement CLI provider seam that uses a default keyless provider registry, resolves provider keys from RELAVIUM_<PROVIDER>_API_KEY, and computes neededProviderIds for pre-flight checks.
  • Introduce minimal plain-text and NDJSON run-event renderers behind a RunRenderer seam, with tests ensuring lifecycle-focused output and NDJSON round-tripping.
apps/cli/src/workflows/resolve.ts
apps/cli/src/workflows/resolve.test.ts
apps/cli/src/commands/inputs.ts
apps/cli/src/commands/inputs.test.ts
apps/cli/src/engine/providers.ts
apps/cli/src/engine/providers.test.ts
apps/cli/src/render/renderer.ts
apps/cli/src/render/renderer.test.ts
Build a node-backed CLI execution host and fail-closed tool/agent wiring into the core engine.
  • Add createCliHost that provides UUID ids, ISO timestamps, in-memory run store and checkpointer, AbortController, and timer utilities for the engine.
  • Implement buildEngine that wires the CLI host, expression sandbox, standard node executor (including agent and human gate handlers), and a fail-closed ToolHost (empty capabilities) with injectable host/provider options.
  • Wire CLI to depend on @relavium/core and @relavium/llm packages in its package manifest.
apps/cli/src/engine/host.ts
apps/cli/src/engine/build-engine.ts
apps/cli/package.json
Add default keyless provider registry to @relavium/llm and expose it via the public API.
  • Introduce defaultProviders factory that returns a Record<ProviderId, LlmProvider> mapping for anthropic, openai, deepseek (via OpenAI adapter), and gemini, with no vendor SDK types exposed.
  • Export defaultProviders from the @relavium/llm public index for host-side provider resolution seams.
packages/llm/src/providers.ts
packages/llm/src/index.ts
Update CLI program construction and roadmap/docs to reflect Phase-2 status and 2.D behavior.
  • Extend buildProgram to accept a BuildProgramOptions including suppression of commander stderr and optional CommandContext, and pass this context into registerCommands.
  • Adjust CLI tests that previously assumed run was a stub to now use another stub command (create) for not-implemented behavior and JSON error envelope assertions.
  • Update roadmap and reference docs to mark 2.A/2.B as done, describe relavium run implementation status, document --input coercion, SIGINT behavior, gate exit codes, provider key pre-flight, and fail-closed ToolHost, and mark the secret-in-prompt obligation as resolved.
  • Refresh top-level README and CLAUDE status docs to indicate Phase 2 CLI work is in progress rather than merely unblocked.
apps/cli/src/program.ts
apps/cli/src/run.test.ts
docs/roadmap/deferred-tasks.md
docs/roadmap/current.md
docs/reference/cli/commands.md
docs/roadmap/phases/phase-2-cli.md
README.md
CLAUDE.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d05c428c-5912-4c36-9839-f18675d19dd6

📥 Commits

Reviewing files that changed from the base of the PR and between c3b893b and f3d5915.

📒 Files selected for processing (1)
  • apps/cli/src/commands/inputs.ts

📝 Walkthrough

Walkthrough

Implements the relavium run CLI command (workstream 2.D) end-to-end. Adds a default LLM provider registry in @relavium/llm, then builds the CLI execution stack: a Node-backed execution host, engine builder, workflow file resolver, input parser/type coercer, NDJSON/plain event renderers, and the runCommand orchestrator. Wires everything through specs.ts, program.ts, and the CLI entry point. Updates CLI reference docs and roadmap status.

Changes

relavium run command (workstream 2.D)

Layer / File(s) Summary
Core parser export for size limit
packages/core/src/parser.ts, packages/core/src/index.ts
MAX_SOURCE_CHARS is exported from the parser and re-exported from the package index so external hosts can enforce the same pre-read size guard used internally by parseWorkflow.
LLM default provider registry
packages/llm/src/providers.ts, packages/llm/src/index.ts
defaultProviders() returns an immutable registry mapping anthropic, openai, deepseek, and gemini to keyless adapter instances; re-exported from the package index.
CLI provider seam, execution host, and engine builder
apps/cli/package.json, apps/cli/src/engine/providers.ts, apps/cli/src/engine/providers.test.ts, apps/cli/src/engine/host.ts, apps/cli/src/engine/build-engine.ts
Adds workspace dependencies for @relavium/core and @relavium/llm. ProviderResolver, neededProviderIds, createProviderResolver, and providerKeyEnvVar form the provider seam for API-key pre-flight validation. createCliHost builds a Node-backed ExecutionHost with wall-clock time, UUID generation, in-memory store, and timers. buildEngine assembles host, provider resolver, expression sandbox, tool registry (fail-closed with empty host), and standard node executor into a WorkflowEngine. Tests validate provider ID collection rules and env var name mapping.
Workflow file resolver
apps/cli/src/workflows/resolve.ts, apps/cli/src/workflows/resolve.test.ts
resolveWorkflowSource resolves a <workflow> CLI argument to an absolute path + YAML string, supporting explicit paths and bare IDs via candidate discovery under <projectConfigDir>/workflows/. Enforces a 2 MiB pre-read cap, distinguishes missing from unreadable files, and throws typed CliError('invalid_invocation') on failure. Tests cover path variants, error message specificity, and size enforcement.
CLI input parsing and type resolution
apps/cli/src/commands/inputs.ts, apps/cli/src/commands/inputs.test.ts
parseInputArgs converts --input key=value tokens to a raw string map, throwing on malformed tokens. resolveInputs validates and coerces inputs against the workflow definition's declared types, rejecting unknown/missing-required inputs and strictly validating number/boolean coercion. Tests cover all parse and coercion edge cases.
Plain and JSON event renderers
apps/cli/src/render/renderer.ts, apps/cli/src/render/renderer.test.ts
RunRenderer interface with createJsonRenderer (NDJSON, one line per RunEvent) and createPlainRenderer (terse lifecycle text via internal describe() switch, silencing non-lifecycle detail events). Tests verify formatting for lifecycle, failures, gate pauses, cancellation, and correct suppression of token/cost events.
runCommand orchestrator
apps/cli/src/commands/run.ts, apps/cli/src/commands/run.test.ts
runCommand resolves config, parses workflow, resolves inputs, pre-flights required provider keys, builds and starts engine, drives event stream through the selected renderer with cooperative SIGINT cancellation, and returns deterministic ExitCode. nextOutcome maps terminal engine events. Tests cover success/JSON/failure modes, invalid inputs, missing files, malformed YAML, gate pauses, SIGINT hygiene across sequential runs, and missing API key pre-flight.
CLI wiring: specs, program, and entry point
apps/cli/src/commands/specs.ts, apps/cli/src/program.ts, apps/cli/src/run.ts, apps/cli/src/run.test.ts
CommandContext and STUB_COMMANDS added to specs.ts; registerRun calls runCommand when context is provided or stubs cleanly when absent. BuildProgramOptions with optional context added to program.ts. run.ts entry point gains resolveGlobalOptions pre-step and threads a shared result holder so subcommands emit their ExitCode. Stub test cases updated to invoke create.
CLI reference docs and roadmap status
docs/reference/cli/commands.md, docs/roadmap/current.md, docs/roadmap/deferred-tasks.md, docs/roadmap/phases/phase-2-cli.md, CLAUDE.md, README.md
Documents SIGINT cancel, pre-flight API key validation with env var names, and human-gate pause contract in run reference. Marks Phase 2.A/2.B done via PR #40 and 2.D in progress. Closes secret-input taint-check deferred item; documents fail-closed ToolHost as security follow-up. Updates project status wording.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant run as run() entry
    participant runCommand
    participant resolveWorkflowSource
    participant resolveInputs
    participant neededProviderIds
    participant createProviderResolver
    participant buildEngine
    participant WorkflowEngine
    participant RunRenderer

    User->>run: argv [run, workflow.yaml, --input k=v]
    run->>run: resolveGlobalOptions
    run->>runCommand: RunCommandArgs, RunCommandDeps
    runCommand->>resolveWorkflowSource: workflowArg, {cwd, projectConfigDir}
    resolveWorkflowSource-->>runCommand: {path, yaml}
    runCommand->>resolveInputs: WorkflowDefinition, raw inputs
    resolveInputs-->>runCommand: typed Record
    runCommand->>neededProviderIds: WorkflowDefinition
    neededProviderIds-->>runCommand: ProviderId[]
    runCommand->>createProviderResolver: keyFor pre-flight per provider
    createProviderResolver-->>runCommand: API key validated
    runCommand->>buildEngine: {host?, providers?}
    buildEngine-->>runCommand: WorkflowEngine
    runCommand->>WorkflowEngine: start(def, inputs)
    loop engine event stream
        WorkflowEngine-->>runCommand: RunEvent
        runCommand->>RunRenderer: onEvent(RunEvent)
        RunRenderer-->>User: plain text or NDJSON line
    end
    runCommand-->>run: ExitCode
    run-->>User: process exit
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • HodeTech/Relavium#6: Introduces the RunEvent schema and lifecycle events (run:paused, run:completed, run:failed) — the canonical event types and structures that this PR's renderers consume and validate against.
  • HodeTech/Relavium#40: Establishes the CLI Phase 2 skeleton (2.A/2.B) including the command registry wiring and entry-point plumbing that this PR builds upon and evolves with runtime context threading.

Poem

🐇 A rabbit hops in, types relavium run,
The workflow resolves and the engine's begun!
With SIGINT to cancel and keys pre-flighted,
Each gate pause and JSON line tidily cited.
Plain text for humans, NDJSON for machines —
The CLI now works. Thump thump! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: wiring the relavium run command to the core engine, which is the primary focus of this PR implementing phase 2.D.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The workflow size cap in resolveWorkflowSource is hard-coded as MAX_WORKFLOW_BYTES mirroring the core parser’s MAX_SOURCE_CHARS; consider centralizing this limit (or importing from a shared module) so future changes to the parser’s cap don’t silently desync the CLI’s pre-read check.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The workflow size cap in `resolveWorkflowSource` is hard-coded as `MAX_WORKFLOW_BYTES` mirroring the core parser’s `MAX_SOURCE_CHARS`; consider centralizing this limit (or importing from a shared module) so future changes to the parser’s cap don’t silently desync the CLI’s pre-read check.

## Individual Comments

### Comment 1
<location path="apps/cli/src/commands/inputs.ts" line_range="74-80" />
<code_context>
+      return parsed;
+    }
+    case 'boolean': {
+      if (value === 'true' || value === '1') {
+        return true;
+      }
+      if (value === 'false' || value === '0') {
+        return false;
+      }
+      throw new CliError('invalid_invocation', `input '${name}' must be a boolean (true/false).`);
+    }
+    default:
</code_context>
<issue_to_address>
**nitpick:** Align the boolean error message with the accepted values (true/false and 1/0), or narrow the parser.

Right now the parser accepts both `true`/`false` and `1`/`0`, but the error text only mentions `true/false`. Please either update the message to match the accepted inputs or remove the numeric aliases so the diagnostics stay accurate.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread apps/cli/src/commands/inputs.ts Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the core 'run' command ('relavium run') for the CLI (Workstream 2.D), integrating it with @relavium/core and @relavium/llm. Key additions include workflow path/ID resolution, input parsing and coercion, pre-flight provider key validation, a Node-backed execution host, event rendering (plain text and NDJSON), and cooperative cancellation via SIGINT. The feedback suggests a minor refactoring of the 'sleep' helper in 'build-engine.ts' to use a more concise, idiomatic one-liner promise syntax.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread apps/cli/src/engine/build-engine.ts Outdated
Comment on lines +43 to +46
sleep: (ms) =>
new Promise((resolveSleep) => {
setTimeout(resolveSleep, ms);
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The sleep implementation can be made more concise. This one-line version is a common and idiomatic way to create a promise-based sleep function in TypeScript, improving readability.

    sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),

cemililik and others added 4 commits June 22, 2026 13:58
The pre-parse character cap was a parser-local const. Export it so a host -- the
CLI's resolveWorkflowSource pre-read byte guard -- can apply the SAME limit rather
than hard-coding a copy that could silently desync from this authoritative cap.

Refs: PR #41 review

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- resolve.ts: reuse @relavium/core's exported MAX_SOURCE_CHARS as the pre-read byte
  ceiling instead of a hard-coded copy, so the CLI guard never desyncs from the
  parser's authoritative cap (review: centralize the limit).
- inputs.ts: the boolean coercion accepts true/false AND 1/0, so the error message
  now matches ("must be a boolean (true/false or 1/0)").
- build-engine.ts: collapse AgentRunnerDeps.sleep to the idiomatic one-liner.
- run.test.ts: throw TypeError (not Error) from the SIGINT-handler type-check guard
  (Sonar — a failed type check should raise TypeError).

Refs: PR #41 review

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A final multi-dimensional review (9 dimensions, every finding adversarially
verified) returned 0 blockers/high/medium; this folds in the confirmed low/nit
findings:

- inputs: reject a repeated `--input k=…` key (fail-fast, not silent last-wins);
  reject JS radix literals for a number input (`--input n=0x10` no longer silently
  becomes 16) while still accepting decimal/negative/scientific forms.
- run: the run:paused→exit-3 mapping is correct for 2.D (a human gate is the only
  run:paused source — no media host is wired); tightened the comment and recorded
  the media-only-park revisit as a 2.S follow-up in deferred-tasks.
- providers/commands.md: stop mis-attributing the RELAVIUM_<PROVIDER>_API_KEY name
  to config-spec.md (it homes the precedence layer, not the env-var name).
- tests: add the symmetric pre-flight PASS case (a present key builds the engine,
  proving no false-fail), a non-anthropic (gemini) missing-key case, and the
  gate-terminal NDJSON line under --json; harden the SIGINT test to identify
  run.ts's handler by set-delta (not .at(-1)) and correct a once-listener comment;
  annotate the providers.test parse() return type.

Toolchain green (lint/typecheck/test 110, build, format, seam fence, engine-deps);
Leakwatch clean on the new code.

Refs: PR #41 review

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ES2022 modernization — replace Object.prototype.hasOwnProperty.call(out, key)
with the clearer Object.hasOwn(out, key) (Sonar es2022).

Refs: PR #41 review

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cemililik
cemililik merged commit 0c98abe into main Jun 22, 2026
8 of 9 checks passed
@sonarqubecloud

Copy link
Copy Markdown

cemililik added a commit that referenced this pull request Jun 22, 2026
PR #41 merged `relavium run` wired to @relavium/core (2.D — the M3 keystone) plus
the `defaultProviders()` seam registry and the exported `MAX_SOURCE_CHARS` cap.
Flip the status surfaces now that it's on main (roadmap-done-after-merge):

- phase-2-cli.md: 2.D heading → ✅ Done (PR #41); status line advances the spine
  pointer to 2.F and notes the 2.E (ink TUI) + 2.H (durable history) run-surface
  feeders now open.
- current.md: record 2.D Done (PR #41); "Next on the spine" → 2.F then 2.K (M3).
- CLAUDE.md / README.md: status paragraphs note `relavium run` has landed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant