Skip to content

Latest commit

 

History

History
822 lines (663 loc) · 34 KB

File metadata and controls

822 lines (663 loc) · 34 KB

runx reference

Architecture and contributor reference for the runx OSS repo: the CLI surface, the trusted Rust local runtime, generated contracts, extension protocols, SDKs, harness, local receipts, registry, official skills, and packaging. For the overview and quickstart, start at the README.

The npm CLI package is @runxhq/cli and exposes the runx binary.

Your First Skill In 5 Minutes

Start with the checked-in hello-world skill:

cargo build --manifest-path crates/Cargo.toml -p runx-cli
export RUNX_RECEIPT_DIR="$(mktemp -d)"
crates/target/debug/runx skill examples/hello-world \
  --message "hello from docs" \
  --non-interactive \
  --json

On macOS 26, complete the Developer Tools permission prerequisite if the initial build stalls.

When no production signer environment is configured, local runx skill and inline runx harness runs seal local-development receipts. Publishing and hosted verification require real authority.

Then inspect the emitted receipt. The full walkthrough is in getting-started.md, and the next step is skill-to-graph.md. For governed code changes, see issue-to-pr.md.

Zero-Funded Payment Dogfood

Run the local payment dogfood lane without a funded wallet, hosted account, or provider keys:

pnpm x402:dogfood:local

This proves runx payment authority, refusal, receipt signing, offline receipt verification, and the documented x402 upstream/x402-rs/CDP/Stripe SPT preflight shape. It does not claim real x402 settlement or a real Stripe charge; live conformance lanes require dedicated funded testnet wallets or provider test credentials. No-secret preflight reports use can_run: false and missing_env to name live blockers without printing secret values. See docs/demos.md for the full split between local dogfood and live protocol conformance.

Requirements

Native CLI:

  • Rust 1.97+
  • The native Rust CLI path must stay useful without Node, pnpm, tsx, or TypeScript packages installed.

Workspace and npm wrapper:

  • Node.js 20+
  • pnpm 10+

Install For Development

pnpm install
pnpm build
pnpm test
pnpm typecheck
pnpm verify:fast
pnpm rust:check

Contributor setup, test selection, and commit sign-off rules are in CONTRIBUTING.md.

Local CLI

For a live creator workflow, link the global runx binary to this checkout once:

pnpm cli:link-global

Then invoke the linked runx binary from anywhere. Use explicit paths outside a runx workspace; bare skill names resolve from the current workspace's skills/ directory.

runx --help
runx skill /path/to/runx/oss/fixtures/skills/echo --message hello --json
cd /path/to/runx/oss
runx skill ./skills/skill-lab design --objective "build sourcey docs skill" --json

Recommended flows:

runx init
runx init -g --prefetch official
runx new docs-demo --objective "Create a bounded documentation decision skill"
runx list skills
runx registry search sourcey --json
runx skill sourcey/sourcey@1.0.0 --registry https://runx.example.test --project . --json
runx add sourcey/sourcey@1.0.0 --registry https://runx.example.test --to ./skills --json
runx skill issue-to-pr --fixture /path/to/repo --task-id task-123
runx resume <run-id> answers.json
runx history <receipt-id> --json
runx history
runx mcp serve ./fixtures/skills/echo
runx skill ./skills/skill-lab design --objective "build github review skill"
runx harness ./fixtures/harness/echo-skill.yaml
runx config set agent.provider openai
runx config set agent.model gpt-5.1
printf '%s' "$OPENAI_API_KEY" | runx config set agent.api_key --from-stdin

The resume file keeps agent work and human authorization distinct:

{
  "answers": {
    "agent_task.example.output": {
      "result": {}
    }
  },
  "approvals": {
    "provider-effect:example": {
      "approved": true,
      "reason": "I authorize this exact provider action."
    }
  }
}

Only entries under approvals carry host-attested human approval provenance. The local CLI cannot authenticate a person from JSON: by placing a decision there, the host asserts that a human approved the exact pending gate. An agent must never author or promote that entry; it returns the pending approval to its human or to an integrated host that authenticates the decision. Never place a consequential decision under answers; that section records agent or caller-supplied work and cannot resolve an approval gate. Runx rejects an agent response presented to an approval request. The pending run's answers_template contains the exact request ids to resolve.

Hosted and replay drivers may bind runx skill or runx resume with --package-digest and --execution-closure-digest. Runx recomputes both at the native execution boundary, persists them in pause checkpoints, and rejects a resume whose supplied bindings disagree with that checkpoint. These flags bind execution; they are never skill inputs. The closure digest includes the immutable Runx release identity, so a pending continuation cannot silently cross a runtime upgrade. Runx-generated run ids and durable graph state bind the same package and execution-closure digests, so changing a skill, transitive child package, or runtime closure cannot merge incompatible checkpoint history.

Execution ceilings remain owned by the capability they constrain rather than being flattened into one misleading global number: an MCP call timeout, an outbox timeout, a native command argument budget, and a file-bundle budget are different contracts. Their constants use capability-qualified names. Limits that are selectable per invocation or materially shape a sealed execution are typed at the invocation boundary; deterministic-module limits and any limit hit are copied into signed receipt metadata.

Credential redaction and untrusted-data admission are also distinct. Exact values delivered by Runx enter the run-scoped taint set and are scrubbed at every output boundary. External configuration and provider output can contain raw material Runx never delivered, so those narrow boundaries additionally reject exact secret-field names before the data can enter receipts. That check is not a general text heuristic and never removes URLs or unrelated operator diagnostics.

With agent.provider, agent.model, and agent.api_key configured, the CLI can now resolve managed agent work directly. Deterministic tools, approvals, and required human inputs keep their existing local behavior.

Prepared context never asks for performative approval: it binds the selected skill, inputs, execution closure, and drift guards into the receipt. A consequential action stops once at its owning boundary—an effect-owned gate for native provider mutation, or an explicit graph approval when no native effect owns the decision. There is no persistent or environment-based auto-approval override; development configuration must not silently acquire live authority.

Provider-backed skills declare requirements in X.yaml; configure them with runx credential or an ignored workspace .env. See Credential Resolution for the exact precedence and storage contract.

Ctrl-C (or the terminal's configured interrupt shortcut) interrupts the whole active Runx context, including supervised tool, JavaScript, adapter, and MCP child process groups. Runx allows a two-second cleanup window, exits with status 130, and treats a second interrupt as an immediate exit. On macOS, Cmd-C is normally copy; Ctrl-C is the interrupt.

Skill result JSON

runx skill ... --json and runx resume ... --json return the canonical runx.skill_run.v1 envelope. A sealed run has one caller-facing result, a compact trace for graph runs, the terminal closure, and the receipt_id needed for independent inspection:

{
  "schema": "runx.skill_run.v1",
  "status": "sealed",
  "skill_name": "example",
  "run_id": "run_example_123",
  "receipt_id": "sha256:...",
  "closure": { "disposition": "closed" },
  "result": {},
  "trace": {
    "graph": "example-read",
    "status": "succeeded",
    "steps": [
      {
        "step_id": "read",
        "skill": "provider.read",
        "status": "success",
        "receipt_id": "sha256:..."
      }
    ]
  }
}

result is the selected runner's normalized output or the declared contract of a graph's terminal successful producer. trace deliberately carries references rather than step payloads. Intermediate graph state, child receipts, and the full signed receipt are written to the local Runx state and receipt stores; process output identity is committed by the receipt. Inspect proof through runx history and runx verify instead of paying to copy it through every agent response. Paused runs keep their exact requests and resume instructions.

Structured input documents

Use per-key --input and --input-json flags for small interactive calls. For generated or reusable input, pass the complete runner input object through one workspace-contained JSON document:

runx skill ./skills/research research --inputs request.json --json
cat request.json | runx skill ./skills/research research --inputs - --json

--inputs is exclusive with every per-key input form. A file path is resolved relative to the invocation workspace and must identify one contained, regular, UTF-8 file. File and stdin input are capped at 64 MiB, must decode to exactly one JSON object, and then enter the same selected-runner defaulting and type validation as inline inputs. Runx does not echo a rejected document in its diagnostic.

This is a control-document transport, not a high-volume execution profile. Put large immutable content behind the digest-bound artifact/page boundary and put durable histories behind data cursors rather than carrying either through graph context.

The global link points at packages/cli in this checkout. Rebuild with pnpm build; do not reinstall.

Package Topology

Rust owns the trusted local runtime path. The Rust crate graph is the enforced boundary map:

  • runx-contracts: domain-neutral Rust contract types plus shared schema emission and reconciliation; domain crates contribute the contracts they own.
  • runx-core: pure state-machine and policy decisions.
  • runx-parser: pure skill, graph, runner, and tool manifest parsing.
  • runx-receipts: canonical receipt model, hashing, signatures, and tree verification.
  • runx-runtime: impure local runtime, adapters, exact process invocation and supervision, harness replay, journals, registry clients, payment gates, MCP, and execution.
  • runx-cli: native runx binary over the runtime.
  • runx-sdk: blocking CLI-backed SDK over stable contracts.

The TypeScript package graph is the client, protocol-extension, wrapper, and generated-contract layer:

  • @runxhq/contracts: generated validators and TypeScript types over the Rust-owned schema artifacts.
  • @runxhq/cli: npm distribution wrapper and client presentation around the native CLI.
  • @runxhq/contracts, @runxhq/extension-sdk, @runxhq/host-adapters, and @runxhq/langchain: generated contracts plus narrow process/protocol, host-presentation, and bridge packages over language-neutral contracts.

For the generated package export index, see docs/api-surface.md.

runx-runtime is the canonical local runtime. It owns local skill, graph, harness, receipt, history, policy, authority, payment, typed execution-boundary evidence, MCP, built-in adapter execution, and external execution-adapter supervision for the native CLI path.

TypeScript remains for generated contracts, CLI/client wrappers, cloud/product integrations, host adapters, and protocol helper SDKs over language-neutral protocols. Host adapters can shape host responses over the runx host protocol; they do not own local execution. External execution adapter authors target manifests and wire protocols, so they do not need Rust, runx-core, runx-runtime, or a fork of the core repository. Source-event ingress, hosted runtime binding, catalog/read-model access, and thread/outbox provider adapters are separate protocol lanes, not reasons to broaden the execution-adapter protocol into a second runtime.

Command-surface ownership:

Surface Canonical owner TypeScript role
runx skill local execution runx-runtime::execution via runx-cli npm launcher/client wrapper
runx harness <fixture.yaml> Rust harness replay tests and wrapper views
receipts and history Rust receipt store and journal display/client views
policy, authority, payment, x402 Rust core/runtime policy published type mirrors and product UX
governed data operations typed native operations plus external provider adapters generated types, helper SDKs, provider glue
external execution-adapter protocol runx-runtime supervisor generated types, helper SDKs, host/client wrappers
non-execution extension protocols lane-specific Rust/cloud owners generated types, helper SDKs, provider glue
skill authoring and runx new runx-runtime authoring service plus skill-lab npm launcher only
marketplace and docs projection Rust-owned catalog/contracts generated docs and client views

Stateful product work should use the governed data-plane shape in docs/governed-data-plane.md: domain skills own meaning, while provider adapters execute bounded reads, append-only event writes, and projection reads.

Graphs call exact native operations: data.append_event, data.read_events, data.read_projection, and data.list_stream_heads. Runx resolves each operation's data_source_ref through RUNX_DATA_SOURCES or .runx/data-sources.json. Unbound local://... refs use native durable SQLite; a binding may route the same operation to a conforming external provider such as data.redis. Binding metadata is runtime-owned and is not part of the native operation's public input schema.

Legacy native SQLite stores migrate only through the offline runtime service:

runx data migrate \
  --database .runx/data/events.sqlite \
  --source local://events \
  --json

The command requires exclusive access, accepts only workspace-relative paths, creates a SQLite-consistent backup before changing a recognized legacy schema, rebuilds stream heads and digests, and independently verifies counts and readback. A current store returns an idempotent current proof without another backup. An unknown or partial schema is left byte-identical and fails with recovery guidance.

Large local inputs use artifact.admit and artifact.read. Admission snapshots one contained regular file, binds its media type, byte count, and whole-file digest to an opaque invocation-scoped reference, and never exposes the host path. The current total admission ceiling is 512 MiB. Reads return exact offsets, range and whole-file digests, next_offset, and eof in pages of at most 4 MiB using base64, character-safe UTF-8, or JSON-array record framing; the default page is 1 MiB. The 4 MiB limit is a page ceiling, not a total-file ceiling. fs.read and fs.read_bundle share the same containment and hashing owner for bounded text; they are not alternate large-file transports.

Local Process Boundary

cli-tool, MCP, and external-adapter process sources are trusted host code. Their exact non-secret configuration is declared through environment.required and environment.optional; credentials use the separate credential contract. Runx controls exact argv, cwd, delivered environment, stdin, timeouts, bounded output, process groups or Job Objects, kill-tree behavior, and cleanup.

Receipts record trusted_host_process. That is an honest observation, not a filesystem, network, or syscall-confinement claim. Capability and provider scopes still govern the native or hosted operations Runx performs on the skill's behalf; a local subprocess cannot turn those strings into OS permissions.

JavaScript modules use a different boundary. Their worker receives no ambient OS environment, credentials, workspace path, network API, process API, clock, or randomness; only the validated in-memory module closure, JSON input, exact declared non-secret environment, and fixed limits cross the worker protocol.

Capability Packs

Runx is the generic execution engine. Product workflows stay outside the runx CLI and ship as local skills, runners, and tools in the consuming repo.

The intended extension model is:

  • runx owns generic runtime, thread, outbox, receipt, and handoff machinery
  • service repos own their product workflows as local capability packs
  • operators execute those workflows through normal skill invocation
  • CLI, API, and GitHub-comment triggers all normalize into the same capability execution envelope, while the thread stays the review/control object

Sourcey is the reference shape for this model: from inside the Sourcey repo, runx skill ./skills/outreach status --issue ... resolves the local skills/outreach capability pack. outreach is not a privileged engine command, and there is no privileged runx docs ... path inside the engine.

issue-to-pr follows the same boundary. runx owns the generic source-thread to scafld to PR machinery; service repos own Slack, Sentry, owner assignment, and publish policy. See docs/issue-to-pr.md.

Standalone Skill Packages

runx new <name> --objective <outcome> is a thin client of the canonical Skill Lab build lane. It inspects the target and catalog, requests a closed architecture decision and change draft, binds both to the inspected package digest in native code, stages and harnesses the exact candidate, then applies the same bytes transactionally. The command owns no templates and never adds a placeholder JavaScript module:

runx new docs-demo --objective "Create a bounded documentation decision skill"

Without --managed-agent, the command stops at needs_agent, prints an answers template and exact runx resume command, and leaves the target untouched. Add --managed-agent only when this run should use the configured in-process model loop. To cold-start without installing Runx first, invoke the same command through npx @runxhq/cli.

Community skills should be authored and published as standalone packages through this lane. The main runx repo is the first-party lane for official skills and runtime code, not the community package catalog.

Prefer declarative graphs composed from native tools and existing skills. When irreducible deterministic domain computation needs JavaScript, declare type: javascript; the module receives resolved inputs and returns JSON while Runx owns the process protocol. A frozen second argument carries exact manifest-declared non-secret environment values when needed. Use cli-tool only for a real executable or protocol boundary. See Skill Author Runtime Contract.

Registry search and install now normalize public trust into three tiers: first_party, verified, and community. Richer provenance and attestation metadata still travels with the registry row, but the user-facing install/search surface stays readable.

runx registry package <SKILL.md|skill-dir> --json projects the exact parser-owned publish artifact without writing a registry row. Hosted and third-party publishers should consume that output instead of implementing their own package-file discovery.

Use skill-catalog.md for the maintained category list, catalog search flow, and duplicate-check standard before proposing a new first-party skill.

Skill And X Model

Executable skills split authored skill content from execution profiles. X.yaml is the runx execution profile file; the short name is public compatibility for existing skill packages, but docs and code should describe it as the execution profile:

skills/sourcey/
  SKILL.md
  X.yaml

Direct execution accepts the package directory or SKILL.md inside it. Flat foo.md skill files are no longer a supported execution surface.

In a workspace that owns a skills/ catalog, a root SKILL.md is the workspace operator manual exported to agents; it is not an executable package boundary around the repository. Executable package discovery starts at skills/*/SKILL.md. A standalone workspace root remains a package, and a root X.yaml explicitly opts a catalog workspace root into package ownership.

Execution profiles use a strict YAML subset: no anchors, aliases, merge keys, custom tags, multi-document markers, duplicate mapping keys, or unknown profile fields. Keep capability and receipt mappings explicit in the runner that uses them.

Every graph runner declares its intentional public result producers:

graph:
  name: publish-and-readback
  result_from: [readback]
  steps:
    - id: approve
      run:
        type: approval
    - id: publish
      tool: provider.mutate
    - id: readback
      tool: provider.read

result_from is not a list of graph leaves. It returns each selected successful step's complete declared contract, including packet envelopes. Approvals and intermediate evidence remain in the separate operator context and signed receipts. Multiple names are for mutually exclusive result branches or deliberately combined, non-overlapping contracts; two successful producers may not emit the same key. Preparation resolves every result producer through its exact runner, tool manifest, or nested skill and rejects a missing semantic output contract before any step executes. Declare run.outputs, artifacts.wrap_as, or artifacts.named_emits; transport output is never an implicit graph result.

Public catalog packages must keep examples in standalone fixtures, not inline manifest harness blocks. The package should contain only the files the skill uses at execution time: SKILL.md, X.yaml, deterministic runner files, schemas, fixtures, and narrowly scoped context/ or references/. Do not add README/changelog/setup docs, generated state, logs, screenshots, private provider config, or broad project plans inside a public skill. The public docs for the package belong in SKILL.md; external guides belong under docs/.

See ../docs/skill-profile-model.md for resolution rules, publication modes, trust tiers, MCP export, and composite skill behavior.

Use work-plan for bounded change planning, skill-lab for skill-package design and improvement, and the named downstream execution lane for mutation.

Tool Authoring

Reusable Runx mechanics and security boundaries belong in the native Rust tool catalog. Skills should compose those native tools instead of shipping request, filesystem, Git, hashing, packet, approval, credential, or receipt wrappers. Use runx tool inspect <name> --json to inspect the contract that both graphs and managed agents execute.

The repository-level tool package shape remains for a genuinely separate executable or protocol boundary that should be callable as a tool:

tools/<namespace>/<tool>/
  src/index.mjs
  fixtures/*.yaml
  manifest.json
  run.mjs

manifest.json is the execution contract and sole owner of the tool name, source, inputs, defaults, artifact projection, scopes, and governance metadata. Process code contains only the domain behavior the native catalog cannot express. defineTool() from @runxhq/extension-sdk may transport the already materialized input object and structured failures over the local process protocol; it does not declare or regenerate the manifest.

runx tool build --all --json
runx dev --lane deterministic --json
runx dev --lane repo-integration --json

When a run.mjs entrypoint is needed, it is a thin process shim and owns no contract metadata. runx tool build is read-only: it validates the source manifest and reports hashes derived from the manifest and its declared source files without rewriting either. Do not create one of these packages merely to wrap a capability already owned by the native catalog. The declared entrypoint must run directly on the supported Node runtime; do not probe for build output or import uncompiled TypeScript.

Official Packages

The official catalog is explicit about why each package is public:

  • canonical governed skills: charge, dispute-respond, skill-lab, review-skill, least-privilege, overlay, policy-author, audit-receipt, refund, ops-desk, send-as, spend, weather-forecast
  • branded provider skills: nitrosend, nws-weather-forecast, stripe-pay, x402-pay
  • context skills: brand-voice, taste-profile

Other bundled packages stay in the same SKILL.md + X.yaml shape, but are internal by default. Internal packages must declare why they remain bundled: graph-stage, runtime-path, harness-fixture, or context. Owned graph stages live below their public skill at skills/<skill>/graph/<stage>/X.yaml, not as root catalog packages.

For first-party skill proposal work, the core builder bar is explicit: proposal packets should name the real pain being solved, explain fit against the current runx catalog, surface maintainer decisions cleanly, and avoid builder residue or placeholder targets.

See docs/operator-console.md for the manager-dashboard model that lets agents operate projects, workspaces, products, or accounts through the same governed lanes as the UI.

Each ships as a portable SKILL.md plus a colocated execution profile at skills/<skill>/X.yaml when it exposes deterministic runners or inline harness coverage. Upstream skills that runx does not own keep their execution profiles under bindings/<owner>/<skill>/X.yaml with adjacent binding.json governance metadata. Bare skill names resolve only to local workspace skills or locked first-party official shorthand. Third-party registry execution uses the explicit owner/name@version form, optionally with --registry and --digest, and only trusted signed registry packages are materialized into the runnable cache. Official skills are registry-backed and cached locally on first acquisition. The npm CLI package no longer needs to ship the official runtime skill bodies for normal execution.

Agent graphs can also demand-load skills as context instead of executing them. Put reusable judgement, operating procedure, or capability skills in the local registry, then reference them from an agent-task step with context_skills:

context_skills:
  - ../taste-profile
  - registry:runx/taste-profile@1.0.0

The runtime injects each referenced SKILL.md as a generic runx.skill.context artifact in the agent invocation current_context. Local path refs resolve relative to the graph; registry refs require RUNX_REGISTRY_DIR and are read from the local registry, not fetched remotely at execution time. Explicit local/file registry sources are treated as user-owned local material: they are digest-checked, but they do not require hosted registry signatures. Context skills are bounded, digest-labeled, and presented to managed agents as untrusted advisory data.

Graph steps can execute local-registry skills too:

steps:
  - id: build_docs
    skill: registry:runx/sourcey
    runner: sourcey

This uses the same explicit local-registry rule: set RUNX_REGISTRY_DIR, sync or publish the skill into that registry first, and treat .runx/registry-step-skills as generated runtime cache rather than source.

Any runnable skill package can also be exposed locally as an MCP tool with:

runx mcp serve ./skills/sourcey

That MCP surface is a thin facade over the normal runx kernel path, so receipts, policy, approvals, and resolution requests still behave the same way.

Receipts

Local receipts are append-only JSON files under .runx/receipts unless RUNX_RECEIPT_DIR is set. runx history verifies receipt signatures and surfaces verified, unverified, or invalid status. Once a skill package and runner resolve, a blocked preparation also seals a blocked refusal receipt; the JSON failure returns its receipt_id and prepared-context digest so rejected input or context admission remains visible in history without echoing the rejected value.

Graph receipt lineage is an immutable one-way DAG. The parent commits each child receipt ID and exact signed-body digest; a reusable child is not re-signed with one lineage.parent. This keeps receipt identity content-addressed and allows the same child proof to participate in more than one graph without a store collision. Runtime tree resolution collapses only identical repeated child receipts into one DAG node; two different signed bodies under the same receipt ID remain ambiguous and fail verification.

Publish a local receipt to the hosted notary with:

runx publish ./.runx/receipts/<receipt-id>.json

runx publish posts the full sealed receipt to POST /v1/receipts/notarize with publish: true, then prints the public /r link and content hash returned by the notary. Configure the hosted API with RUNX_PUBLIC_API_BASE_URL (default https://api.runx.ai) and authenticate with RUNX_PUBLIC_API_TOKEN or --token (or run runx login). Runx requires HTTPS for non-loopback API origins so the bearer token cannot be sent to a public plaintext endpoint.

For local hosted dogfood only, point at a loopback API and opt into the private network escape explicitly:

runx publish ./.runx/receipts/<receipt-id>.json \
  --api-base-url http://127.0.0.1:47882 \
  --token dev-token \
  --allow-local-api

Workspace Policy

Projects can opt into stricter local cli-tool admission with .runx/config.json:

{
  "policy": {
    "strict_cli_tool_inline_code": true
  }
}

When enabled, local execution rejects known inline interpreter and shell eval forms such as node -e, python -c, and bash -lc. Move the program into a checked-in script file and invoke that file instead.

Trainable Exports

Trainable export is currently a TypeScript-maintained projection command. It can project verified receipt lineage into newline-delimited training rows without mutating the original receipts, but it is not yet part of the native Rust CLI surface:

runx export-receipts --trainable
runx export-receipts --trainable --receipt-dir ./.runx/receipts --status complete --source cli-tool

Rows are emitted as JSONL and follow the public training contract published at:

  • https://runx.ai/spec/training/trainable-receipt-row.schema.json

The export keeps receipt identity, verified outcome resolution, ledger artifacts, and runner provenance together so downstream training and eval systems can consume governed lineage instead of raw prompt logs.

Harness

Run a whole skill package to execute its inline X.yaml cases and every conventional fixtures/*.yaml case through the native runtime:

runx harness ./skills/business-ops --json

Pass a fixture YAML path instead when intentionally replaying one standalone case:

runx harness ./fixtures/harness/echo-skill.yaml --json

Fixtures can assert the terminal status, receipt identity and lineage, exact or subset output, ordered graph steps, and exact or subset output for named graph steps. Prefer semantic subsets over complete snapshots:

expect:
  status: sealed
  receipt:
    schema: runx.receipt.v1
    child_receipt_count: 2
  steps:
    - classify
    - act
  step_outputs:
    act:
      subset:
        action_packet:
          data:
        status: awaiting_approval

A receipt-dependent fixture may seed a bounded set of existing receipts from its own package:

setup:
  receipts:
    - fixtures/receipt-store/sha256-example.json

Each path must be a normalized package-relative .json path. Runx parses and verifies every seeded receipt under the fixture's configured signature policy before placing it in that case's isolated store. Seed data is evidence for the case, not proof of a provider action; the fixture must still assert only what the verified receipts and runner output establish.

Supplied caller.answers are semantic oracles, not inert model stubs. Native replay fails unless every supplied answer is observable in the root or step output. For stateful work, use one graph fixture to prove the transition and durable readback; do not depend on fixture filename order.

Package replay uses a disposable project-owned workspace below .runx/harness and a separate receipt store for every case, then cleans that scratch state. Cases therefore cannot satisfy each other through filename order or ambient receipt history. Receipts produced by each case are verified and copied to the durable .runx/receipts store, or to the directory selected by --receipt-dir. An explicit RUNX_CWD identifies the owning project but does not disable harness isolation.

Doctor And Dogfood

For the core first-party skill lane, run:

pnpm dogfood:core-skills

This remains a TypeScript wrapper lane. The native Rust proof for local orchestration is the Rust CLI/runtime test and fixture suite; wrapper dogfood is useful only after the same behavior is proven without Node, pnpm, or tsx.

For the default structural verification lane during refactors, run:

pnpm verify:fast

That lane keeps the cheap workspace checks together: OSS typecheck plus the fast package test surface with the current structural budget and boundary coverage.

Build And Pack

pnpm build
pnpm test tests/cli-package.test.ts
cd packages/cli
npm pack --dry-run --json

The package must include dist/index.js and dist/index.d.ts, and dist/index.js must be executable.

Boundary Rules

  • oss/ (this repository) must not import from cloud/ (the private companion workspace, not part of this checkout).
  • State-machine and policy packages remain pure.
  • Rust owns trusted local runtime/execution, including exact process supervision, receipts, policy, authority, payment, harness, built-in adapters, and external execution-adapter supervision.
  • TypeScript runtime-local and adapters packages must not be fallback executors for trusted local behavior.
  • External execution adapters own their side effects behind language-neutral protocols and manifests; non-execution extension lanes have their own protocol contracts.
  • External extension authors must not need Rust, a runx-core or runx-runtime dependency, or a core repository fork.
  • CLI, SDK, IDE plugin, host adapter, and MCP entrypoints delegate to runner contracts or external protocols instead of duplicating the engine.