Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

CLI reference

All commands available in the harn CLI.

To add a new subcommand or port an existing one off Rust, see Extending the CLI in .harn. For the machine-readable side of --json modes, see the harn --json contract.

harn run#

Execute a .harn file.

harn run <file.harn>
harn run --trace <file.harn>
harn run --profile --profile-json profile.json <file.harn>
harn run --flight-recorder <file.harn>
harn run -e 'harness.stdio.log("hello")'
harn run --deny shell,exec <file.harn>
harn run --allow read_file,write_file <file.harn>
harn run --standalone --allow json_parse policy.harn
harn run --approve-risky git.push release.harn
harn run --no-sandbox <file.harn>
harn run --write-root /path/to/output main.harn
harn run --read-only-root /path/to/other-repo main.harn
harn run --sandbox-write-root /path/to/tool-cache main.harn
harn run --sandbox-read-root /path/to/sdk main.harn
harn run --grant gh_token=secret://gh/token,expose=GH_TOKEN,for=gh open_pr.harn
harn run --environment-policy isolated eval_child.harn
harn run --yes <file.harn>
harn run --explain-cost <file.harn>
harn run --attest <file.harn>
harn run --attest --receipt-out receipt.json <file.harn>
harn run <bundle.harnpack>
harn run --dry-run-verify <bundle.harnpack>
harn run --allow-unsigned <bundle.harnpack>
harn run --resume .harn/workers/worker_...json
FlagDescription
--tracePrint LLM trace summary after execution. Can also be set with HARN_TRACE=1
--flight-recorderSave the bounded, value-free VM code path under .harn-runs/flight-recordings/. Can also be set with HARN_FLIGHT_RECORDER=1.
--flight-recorder-out <path>Write the flight recording to one explicit file. Requires --flight-recorder; Harn doesn't rotate files in a caller-owned directory.
--flight-recorder-max-events <count>Retain the newest 1 to 1,000,000 VM events in memory. Default: 250,000. Requires --flight-recorder.
--flight-recorder-retain <count>Retain the newest 1 to 1,024 recordings in Harn's default directory. Default: 16. Requires --flight-recorder.
--profilePrint a categorical timing breakdown after execution. Can also be set with HARN_PROFILE=1
--profile-json <path>Write the categorical timing breakdown as JSON. Can also be set with HARN_PROFILE_JSON=<path>
--explain-costPrint static LLM token/cost estimates without executing the script
-e <code>Evaluate inline code instead of a file
--resume <handle-or-snapshot>Cold-restore a suspended top-level agent from its persisted worker snapshot
--deny <builtins>Deny specific builtins (comma-separated)
--allow <builtins>Allow only specific builtins (comma-separated)
--eager-project-handlersLoad every project trigger and hook handler module during startup
--project-triggersRegister manifest triggers and reconcile their durable state for this run
--standaloneRun without loading ambient project configuration, skills, handlers, state roots, or manifest-derived authority
--approve-risky <operation>Explicitly authorize one exact risky stdlib operation for this invocation; repeatable (for example git.push)
--no-sandboxDisable the default worktree filesystem/process sandbox and network side-effect ceiling
--allow-process-loopbackAllow confined child processes to serve and connect over IPv4 or IPv6 loopback without opening remote egress. Supported on macOS; other local sandbox backends fail closed.
--allow-process-networkAllow child network while retaining filesystem/process confinement. On macOS, child traffic stays denied until HARN_EGRESS_* or harness.net.egress_policy(...) configures an allow decision through the managed proxy. On other local platforms Harn does not attach that proxy; the grant raises the capability ceiling and children have unrestricted sockets. See Managed child-process egress.
--write-root <path>Write to an extra filesystem root while keeping sandboxing enabled
--read-only-root <path>Read from an extra filesystem root while keeping sandboxing enabled
--sandbox-write-root <path>Let spawned subprocesses write an extra root without granting Harn filesystem builtins access
--sandbox-read-root <path>Let spawned subprocesses read an extra root without granting Harn filesystem builtins access
--sandbox-unix-socket-root <path>Let spawned subprocesses bind and connect Unix-domain sockets whose socket file lives under this root, without opening any IP networking. Build servers (sbt, Gradle, MSBuild) need this. Supported on macOS; other local sandbox backends fail closed.
--environment-policy <inherited|isolated|granted>Choose which launch-time environment values this session and its subprocesses may read. Default: inherited.
--grant <spec>Grant one named value to this session; repeatable. Selects granted when the policy is omitted. See Environment policies and grants.
--yesAccept first-run provider setup prompts, including local Ollama config seeding
--attestEmit a signed provenance receipt after execution
--receipt-out <path>Write the receipt to a specific JSON path
--attest-agent <id>Agent id used to load or create the Ed25519 signing key
--jsonEmit a versioned NDJSON event stream on stdout instead of mixed pipeline output
--quietWhen --json is set, drop stdout and stderr events (transcript/tool/hook/persona/result still flow)
--emit-summary-jsonEmit one terminal run_summary JSON object as a single NDJSON line; defaults to stderr
--summary-file <path>Write --emit-summary-json output to a file instead of stderr
--summary-fd <fd>Write --emit-summary-json output to an already-open Unix file descriptor
--emit-phase-jsonEmit one terminal run_phase JSON object as a single NDJSON line; defaults to stderr
--phase-file <path>Write --emit-phase-json output to a file instead of stderr
--phase-fd <fd>Write --emit-phase-json output to an already-open Unix file descriptor
--emit-rusage-jsonEmit one terminal run_rusage JSON object as a single NDJSON line; defaults to stderr
--rusage-file <path>Write --emit-rusage-json output to a file instead of stderr
--rusage-fd <fd>Write --emit-rusage-json output to an already-open Unix file descriptor
--allow-unsignedWhen running a .harnpack, accept bundles that carry no Ed25519 signature (local-dev override)
--dry-run-verifyWhen running a .harnpack, verify the signature and replay into the cache without executing the entrypoint

Every completed VM invocation through harn run writes an automatic .harn-runs/hxe-....json run record. Harn retains the newest 128 automatic records without deleting workflow or imported records. The record's evidence.execution_id is the same identity attached to its spans and optional flight recording. --json emits evidence_persisted after this record is readable and before the terminal result or error event.

--approve-risky is explicit operator authority, not pipeline configuration. It records a receipt on the protected operation and never relaxes the generic process.exec safety floor or configured command-policy rules.

Project handler startup#

harn run installs project hooks by default so policy remains fail-closed, but does not register manifest triggers or reconcile their durable state unless the run passes --project-triggers. Trigger-oriented commands continue to register the triggers they operate. This makes durable project side effects explicit for ordinary entrypoint runs.

With --project-triggers, Harn validates trigger declarations, handler exports, callable signatures, and predicate signatures before it runs the entry script. Trigger modules and imports initialize on matching dispatch. Project policy hooks validate their declaration shapes at startup but defer export, signature, module, and import resolution until matching dispatch. A matching policy hook that cannot be resolved fails closed; an unrelated broken hook stays off the startup path.

Use --eager-project-handlers to diagnose failures in top-level handler module initialization. It restores fail-fast initialization for every project handler and implies --project-triggers.

Embedded callers select the same contract with commands::run::RunExecutionOptions::project_runtime. Use ProjectRuntimeMode::WithTriggers for lazy trigger dispatch or ProjectRuntimeMode::EagerHandlers for fail-fast handler initialization; the default Project mode keeps hooks active without reconciling durable trigger state. The JSON execution API has the matching execute_run_json_with_options entrypoint. Every in-process entrypoint owns a poll-scoped trigger registry; manifest bindings created by one run are discarded at its boundary while the embedding caller's prior registry is restored unchanged.

Use --standalone for latency-sensitive utilities and policy scripts that must behave the same regardless of their containing directory. Relative and standard library imports still work, and explicit CLI limits such as --allow, --deny, and sandbox roots still apply. Standalone mode does not load a surrounding harn.toml, project skills, project handlers, project state roots, or manifest-derived trusted-host authority. It conflicts with commands whose meaning requires project state, including --eager-project-handlers, --project-triggers, --resume, and --as-job.

Environment policies and grants#

Every launched Harn session has one environment policy:

  • Inherited (the default) captures the launcher's environment once when the session starts. Later changes to the launcher process do not change the session.
  • Isolated admits only the small set of operating-system and toolchain values needed to run commands, such as PATH, temporary-directory settings, locale, and compiler locations. It rejects grants.
  • Granted starts with the same runtime essentials as isolated, then adds only the declared grants.

The policy applies consistently to harness.env, Harn's provider credentials, provider base URLs and regions, Azure/Vertex/Bedrock configuration, and spawned commands. In isolated and granted, platform SDKs cannot silently fall back to home-directory profiles, metadata services, or application-default credentials. Supply the required values as grants or choose inherited.

This policy governs environment variables only. It is a different boundary from the filesystem and process sandbox, which decides which directories a run may touch and what a spawned command may do. See Process sandboxing for that one. Neither boundary widens the other: --no-sandbox leaves the environment policy fully in force, and an environment grant gives no file, network, or tool access. Approval policy is a third, separate thing again.

Each --grant is NAME=SOURCE[,expose=ENV_VAR][,for=COMMAND]:

PartMeaning
NAMEA unique, non-secret name used in receipts and diagnostics.
SOURCEenv:VAR_NAME snapshots that launcher variable at session launch. secret://ACCOUNT/KEY keeps a live secret-store reference, so rotation and revocation take effect without restarting the session.
,expose=ENV_VAROptional. Makes the value available under this unique environment name. Without ,for=, the exposure is session-scoped: harness.env, provider configuration, and every spawned command.
,for=COMMANDOptional. Requires ,expose=. Binds the exposed variable to spawns whose executable basename matches COMMAND (for example gh for /usr/bin/gh). Command-bound grants are invisible in-process — Harn's own harness.llm.call is not an exec — so provider keys stay session-scoped by omitting ,for=.
# Let only `gh` see a vault-backed token; other process.exec calls do not inherit it.
harn run --grant gh_token=secret://gh/token,expose=GH_TOKEN,for=gh open_pr.harn

# Snapshot a provider key from the launcher env, exposed under the same name
# for this run's model calls and every spawned command.
harn run --grant fireworks=env:FIREWORKS_API_KEY,expose=FIREWORKS_API_KEY agent.harn

Duplicate grant names and duplicate expose targets are launch errors. A child session inherits its parent's resolved environment by default. It may narrow that authority (for example, inherited to isolated, or a smaller subset of granted grants), but it cannot widen it or reread the ambient host environment. Ceiling violations return the stable environment_policy.child_exceeds_parent code with the parent and requested policies.

Here “reproducible” has a precise scope: every prompt, worker, and subprocess in one launched session uses the same launch-time environment snapshot, independent of later launcher mutations. Command-bound grants (,for=COMMAND) still draw from that snapshot; they only narrow which spawns receive an exposed variable. The launcher/host establishes the snapshot, and Harn enforces it. This does not claim that two machines have identical environments. Use isolated or explicit grants when a test or replay must not depend on the operator's shell. A live secret:// source may intentionally change after rotation or revocation; its receipt stays stable, but its value is not frozen.

An .env file is not a second policy channel. A launcher may load it before starting Harn; then its values are ordinary launcher environment values and follow the selected policy. A vault or keyring remains the source behind a secret:// grant. Filesystem access to service-account files or other credential files is governed separately by the sandbox.

Process-bootstrap and host-ceiling settings that Harn must read before or above any session—such as the path to a provider catalog overlay, server observability controls, global rate limits, and the host's HARN_LLM_CALLS_DISABLED kill switch—belong to the launcher process and may require a restart to change. A child policy cannot hide or override them. They are not session values.

Every declared grant is disclosed on stderr at launch (its name, source kind, and exact environment target, if any—never the value) and recorded as a non-secret receipt in the --attest provenance log.

The disclosure names only a departure from the launcher's own environment. isolated and granted always announce themselves. The default inherited policy with no grants prints nothing, so a plain harn run leaves stderr clean for --summary-fd and --rusage-fd consumers. The sandbox delta line described in Sandboxing follows the same rule.

Both --environment-policy and --grant are also accepted by harn time run, which shares harn run's confinement surface.

Terminology:

  • Launcher: the process that starts a Harn session, such as the harn CLI or an ACP host.
  • Session: one conversation/execution context and its delegated lineage. ACP defines the session lifecycle; environmentPolicy is a Harn extension.
  • Worker: delegated work inside that lineage. It receives no more environment authority than its parent.
  • Subprocess: an operating-system command started by the session. It sees the same resolved session environment plus explicit per-call overrides.
  • Grant: a named, receipted source-to-environment mapping. Grants are session-wide today.
  • Sandbox: the separate file/process/network boundary.
  • Approval: permission for a risky operation; it does not add environment values.

Exit codes#

A Harn program chooses its own exit status — return 42 and harness.runtime.exit(42) both produce 42, clamped to 0..=255. So no status is unreachable by a program that insists on returning it. Two are nonetheless reserved: Harn documents them, never produces them for a program outcome, and a caller may branch on them.

StatusMeaning
0The program ran and succeeded.
1The program ran and failed, or did not compile.
124Reserved. The run was interrupted or hit a deadline.
125Reserved. Harn could not prepare the run. The program never started.

125 covers everything Harn does on the program's behalf before handing it control: materializing locked dependencies, installing manifest triggers and hooks, loading provider connectors, and launching the session's environment policy. Without it, a harness that shells out to Harn cannot tell "your dependencies could not be prepared" from "your code returned a failure" — a distinction that decides whether to retry, to report an infrastructure fault, or to surface a real result.

Two things that happen before the program runs are deliberately not setup failures, and keep status 1:

  • A compile error. The program's own content failing is the program failing.
  • A .harnpack verification refusal — an absent signature, or one that does not cover the bytes on disk. That is a verdict on the artifact, not a fault in the host that was asked to run it, and a caller reading 125 as "retry the infrastructure" would retry a tampered bundle forever.

harn test uses the same reserved statuses. It materializes the suite's dependencies once before any case runs, so a lock that cannot be prepared exits 125 instead of reporting every case as a failed assertion.

Under --json the terminal error event's code names the phase (package_materialization, manifest_triggers, manifest_hooks, manifest_connectors, entry_unreadable, compile_error, …), so a consumer that wants more than the status does not have to parse prose for it.

--json event stream#

harn run --json <file> writes one JsonEnvelope per line to stdout, each carrying a typed RunEvent. The stream is strictly ordered via a monotonic seq (starts at 1) so a downstream agent can reconstruct the run without parsing prose. The envelope's schemaVersion is 1; see harn --json-schemas for the catalog entry.

Event types (the event_type discriminator lives at data.event_type):

event_typePayload
stdout{ payload: string } — bytes written via print/println/log, verbatim.
stderr{ payload: string } — bytes written via eprint/eprintln.
transcript{ agent_id?: string, kind: string, payload: object } — one entry from the LLM-call transcript stream.
tool_call{ call_id, name, args, started_at } — model-issued tool invocation.
tool_result{ call_id, ok: bool, result } — outcome of a tool invocation.
hook{ name, phase, payload? } — session lifecycle hook fired during the run.
persona_stage{ persona, stage, transition } — persona-stage transition (started / completed / handoff_started / …).
pack_run{ bundle_hash, signature_verified: bool, key_id?: string, cache_hit: bool, dry_run_verify: bool, execution_artifact_state, artifact_fallback_reason?, artifact_decode_ms } — emitted once when harn run <bundle.harnpack> resolves a pack to execute. execution_artifact_state is linked, legacy_v2, or source_fallback.
result{ value, exit_code: int } — terminal event for successful runs.
error{ error: { code, message, details? } } — terminal event when a fatal error prevents a result.

The stream is line-flushed per event. Streaming consumers can pipe through jq -c . for live filtering:

harn run --json examples/hello.harn | jq -c '.data | {seq, event_type}'

Post-run summary JSON#

harn run --emit-summary-json <file> emits one raw JSON object after the run finishes. This is a separate opt-in sink from harn run --json so consumers that need aggregate metrics can keep the event stream contract unchanged. By default the line is appended to stderr after human diagnostics; use --summary-file <path> or --summary-fd <fd> to isolate it from the script's own stderr.

Shape (schema_version: 4):

{
  "schema_version": 4,
  "event": "run_summary",
  "wall_time_ms": 1234,
  "exit_code": 0,
  "llm": {
    "call_count": 2,
    "provider_call_count": 3,
    "input_tokens": 1024,
    "output_tokens": 256,
    "time_ms": 480,
    "cost_usd": 0.0042,
    "known_cost_usd": 0.0042,
    "unpriced_calls": 0,
    "usage_unknown_calls": 0
  },
  "profile": {
    "total_wall_ms": 1234,
    "by_kind": [],
    "residual_ms": 12,
    "top_llm_calls": [],
    "top_tool_calls": [],
    "steps": []
  }
}

profile is present only when the run enables profiling with --profile or --profile-json. The LLM metrics are collected whenever summary JSON is requested, even without --trace.

llm.call_count is the logical-operation count, while llm.provider_call_count includes physical retries. Exact cost_usd becomes null if any physical call is unpriced; known_cost_usd remains the explicit lower bound. A nonzero usage_unknown_calls means the reported token/cache totals are also lower bounds rather than known-complete usage.

Post-run phase JSON#

harn run --emit-phase-json <file> emits one raw JSON object after the run finishes. This uses the same fixed seven-row contract as harn time run --json, but keeps the data on a separate sink so a wrapper can spawn harn run and recover parse/typecheck/compile/setup timings without changing stdout. Use --phase-file <path> or --phase-fd <fd> to isolate the line from stderr.

Shape (schema_version: 2):

{
  "schema_version": 2,
  "event": "run_phase",
  "phases": [
    { "name": "parse", "kind": "top_level", "duration_ms": 12, "input_bytes": 4096 },
    { "name": "typecheck", "kind": "top_level", "duration_ms": 80 },
    { "name": "bytecode_compile", "kind": "top_level", "duration_ms": 35, "cache": "miss" },
    { "name": "run_setup", "kind": "top_level", "duration_ms": 8 },
    { "name": "run_main", "kind": "top_level", "duration_ms": 1200, "events": 14 },
    { "name": "module_compile", "kind": "attribution", "duration_ms": 40, "events": 3 },
    { "name": "module_load", "kind": "attribution", "duration_ms": 75, "events": 8 }
  ]
}

The phase array is always in this order: parse, typecheck, bytecode_compile, run_setup, run_main, module_compile, module_load. On a bytecode-cache hit, parse and typecheck stay present with duration_ms: 0, and the bytecode_compile row flips to "cache": "hit". The final two attribution rows overlap setup/main and are not additive.

Post-run rusage JSON#

harn run --emit-rusage-json <file> emits one raw JSON object after the run finishes containing the total getrusage(RUSAGE_SELF) CPU time sample for the process during that run. Use --rusage-file <path> or --rusage-fd <fd> to isolate the line from stderr.

Shape (schema_version: 1):

{
  "schema_version": 1,
  "event": "run_rusage",
  "cpu_ms": 320
}

You can also run a file directly without the run subcommand:

harn main.harn

By default, harn run installs a worktree sandbox before executing the VM. Filesystem and subprocess cwd access are rooted at the nearest harn.toml project root, or at the invocation working directory when no project manifest is present. Network side effects are denied by the same default policy. Use --no-sandbox only for scripts that need the old unrestricted process behavior; use --read-only-root when a script needs read access to a sibling or shared directory and should remain in the sandboxed profile. The CLI emits a warning when --no-sandbox is used, and rejects --write-root / --read-only-root when --no-sandbox is present — a scoped grant is meaningless once the whole sandbox is off. When a grant is used with the sandbox still active, the CLI prints one line naming exactly the delta (for example sandbox active; extra write root: /path) instead of the blanket --no-sandbox banner, so a routine grant-scoped run does not spam a filesystem/process/egress warning.

Before starting the VM, harn run <file> builds the cross-module graph for the entry file. When all imports resolve, unknown call targets produce a static error and the VM is never started — the same call target ... is not defined or imported message you see from harn check.

The inline -e <code> form is wrapped in pipeline main(harness: Harness, task) { ... } and run as a temp file in the current directory, so:

  • Leading import "..." (and pub import { ... } from "...") lines are hoisted out of the wrapper. They must come first; imports that appear after another statement are not lifted.
  • Relative imports resolve against the working directory: harn run -e
import "./lib"\nlog(answer())' looks for ./lib.harn next to where you invoked harn.
  • Stdlib imports (import "std/...") work the same as in a file.
  • A read-only working directory falls back to the system temp dir; pure-expression -e still works there but relative imports won't resolve.
  • --explain-cost stops after the static pass. It scans direct harness.llm.call(...) and agent_loop(harness, ...) callsites, estimates statically known prompt/system input tokens, applies literal agent_loop iteration caps, and reports unresolved dynamic model or prompt expressions without executing pipeline code.

    When --attest is present, Harn records run start/finish events in an EventLog, stamps each record with prev_hash and record_hash provenance headers, signs the receipt with an Ed25519 key loaded through the configured secret provider chain, and writes the receipt under .harn/receipts/ unless --receipt-out is set.

    Running a .harnpack#

    harn run <bundle.harnpack> accepts a signed content-addressed bundle produced by harn pack. Detection is by .harnpack extension and a zstd magic-byte fallback so renamed bundles still resolve. The runner:

    1. Reads the manifest and embedded contents.
    2. Verifies the Ed25519 signature against the bundle hash through the OpenTrustGraph trust store.
    3. Validates source identities and the schema-v3 linked program or legacy-v2 per-module payloads.
    4. Checks harn_version compatibility — patch mismatches warn, minor or major mismatches refuse the run.
    5. Replays the archive into $HARN_CACHE_DIR/packs/<bundle_hash>/ atomically (the bundle hash is content-addressed, so a second run reuses the unpacked tree).
    6. Installs the schema-v3 linked program without source compilation, or uses the explicit legacy-v2 adapter, then executes with Harness::real().

    The verifier refuses to run an unsigned bundle. Pass --allow-unsigned to opt out (intended for local-dev only). --dry-run-verify performs verification and replay but stops before execution, useful for deployment gates.

    harn parse#

    Parse a .harn file without type-checking or running it.

    harn parse main.harn
    harn parse main.harn --json
    harn parse examples/hello.harn --json | jq .data.kind
    

    Text mode prints the debug AST. --json emits a JsonEnvelope with schemaVersion: 1; data.kind is Program, data.body[] contains tagged AST nodes, and each node carries a byte span.

    harn tokens#

    Tokenize a .harn file.

    harn tokens main.harn
    harn tokens main.harn --json
    

    Text mode prints one token per line with line/column and byte ranges. --json emits a JsonEnvelope with schemaVersion: 1; data[] entries are { kind, lexeme, start, end, line, column }. start and end are byte offsets into the original source.

    harn verify#

    Verify a signed Harn provenance receipt.

    harn verify .harn/receipts/<receipt>.json
    harn verify receipt.json --json
    

    Verification recomputes the EventLog record chain, the receipt event root, the receipt hash, and the Ed25519 signature. It exits non-zero if any receipt event or signature material has been changed.

    harn session#

    List persisted sessions, and export, validate, import, and check canonical session bundles.

    harn session list
    harn session list --session-root /path/to/workspace --limit 10 --json
    harn session export .harn-runs/<run>/run.json --out run.bundle.json
    harn session export .harn-runs/<run>/run.json --local --out run.local.bundle.json
    harn session export .harn-runs/<run>/run.json --replay-only --out run.replay.bundle.json
    harn session validate run.bundle.json --json
    harn session import run.bundle.json --out imported-run.json
    harn session schema --check
    
    SubcommandDescription
    listRead-only. Lists the agent sessions already persisted in .harn/session-store.sqlite, newest first, with status, event count, cost, and title. It does not create or initialize a store. These ids are the input to --from-session on every harn runs subcommand (inspect, view, report, review, export-training).
    export <run-record>Writes a harn_session_bundle envelope. Default output is sanitized; --local preserves local-only content; --replay-only withholds prompt/tool payload fields.
    validate <bundle>Validates required fields, schema version, bundle type, and high-confidence secret markers without writing a run record.
    import <bundle>Validates the bundle and materializes a local run record from replay.run_record or replay metadata.
    schemaPrints, writes, or checks spec/schemas/session-bundle.v1.schema.json.

    export --include-attachments copies artifact payloads into the top-level attachments array. By default attachment payloads are omitted for share safety. import and validate accept --allow-unsafe-secret-markers only for trusted local bundles.

    harn completion#

    Print shell completion scripts.

    harn completion bash
    harn completion zsh
    harn completion fish
    

    Generated completions include subcommands plus static candidates for known provider and model values.

    harn config#

    Inspect, validate, and emit schemas for layered runtime configuration.

    harn config inspect
    harn config inspect --explain
    harn config inspect --config ./harn.config.toml --managed ./org-policy.toml --explain
    harn config validate ./harn.config.toml
    harn config schema --output docs/src/schemas/harn-config.schema.json
    
    CommandDescription
    inspectPrint the redacted merged runtime config
    inspect --explainInclude per-field provenance, shadowed candidates, and managed policy lock status
    validateValidate local, project, or managed overlays against the typed config shape
    schemaPrint the editor JSON Schema for harn.config.toml

    See Layered runtime configuration for precedence, file locations, environment override names, and managed policy examples.

    harn playground#

    Run a pipeline against a Harn-native host module for fast local iteration. harn try is an alias for the same command.

    harn playground --host host.harn --script pipeline.harn --task "Explain this repo"
    harn playground --watch --task "Refine the prompt"
    harn playground --llm ollama:qwen2.5-coder:latest --task "Use a local model"
    harn try "Use local Ollama"
    
    FlagDescription
    --host <file>Host module exporting the functions the script expects (default: host.harn)
    --script <file>Pipeline entrypoint to execute (default: pipeline.harn)
    --task <text>Task string exposed as HARN_TASK during the run
    --llm <provider:model>Override the provider/model selection for this invocation
    --llm-mock <path>Replay LLM responses from a JSONL fixture file instead of calling the provider
    --llm-mock-record <path>Record executed LLM responses into a JSONL fixture file
    --yesAccept first-run provider setup prompts, including local Ollama config seeding
    --watchRe-run when the host module or script changes

    harn playground type-checks the host module, merges its exported function names into the script's static call-target validation, then executes the script with an in-process host adapter. Missing host functions fail with a pointed error naming the function and caller location.

    harn test#

    Run tests.

    harn test conformance                  # run conformance test suite
    harn test conformance tests/language/arithmetic.harn # run one conformance file
    harn test conformance tests/stdlib/     # run a conformance subtree
    harn test conformance --parallel       # run isolated conformance workers
    harn test conformance --parallel -j 4  # pin the conformance worker count
    harn test tests/                       # run user tests in directory
    harn test tests/ --filter "auth*"      # filter by pattern
    harn test tests/ --parallel            # run tests concurrently with bounded workers
    harn test tests/ --parallel --timing   # show progress and slowest tests/files
    harn test tests/ --parallel -j 4       # pin worker count (also via HARN_TEST_JOBS)
    harn test tests/ --affected-from origin/main --parallel # run changed modules' importer tests
    harn test tests/ --affected-from origin/main --plan # print the selection or full-suite fallback as JSON
    harn test tests/fast.harn --test-path tests/contracts.harn --parallel # one curated suite
    harn test tests/ --watch               # re-run on file changes
    harn test conformance --verbose        # show per-test timing
    harn test conformance --timing         # show timing summary without verbose failures
    harn test tests/ --record              # record LLM fixtures
    harn test tests/ --replay              # replay LLM fixtures
    harn test tests/ --coverage            # print per-file line coverage
    harn test tests/ --coverage-out lcov.info  # also write an LCOV tracefile
    harn test --approve-risky git.push tests/release_branch_push.harn
    harn test --trusted-host-dispatch tests/host_routes.harn
    harn test agents-conformance --target http://localhost:8080 --api-key "$KEY"
    

    Watch mode keeps immutable prepared module artifacts warm between reruns. Each test still receives a fresh VM, module state, and persistence root.

    FlagDescription
    --filter <pattern>Only run tests matching pattern
    --target <url>Harness base URL for harn test agents-conformance
    --api-key <key>Bearer API key for harn test agents-conformance; also read from HARN_AGENTS_CONFORMANCE_API_KEY
    --category <name>Agents conformance category to run; repeatable or comma-separated
    --jsonEmit conformance results as JSON to stdout, or the agents-conformance leaderboard report
    --json-out <path>Write user-test results (or the agents-conformance report) to a JSON file; user-test schemaVersion 4 includes typed timeout, phase, named std/timing spans, shard-plan, cost-regression, aggregate, latency-distribution, and captured-output data
    --workspace-id <id> / --session-id <id>Reuse existing Harness resources for agents conformance setup
    --parallelRun a bounded worker pool. User tests run in-process; conformance tests run in isolated processes because each worker owns process-wide runtime state.
    --jobs <N> / -j <N>Maximum concurrent workers (also HARN_TEST_JOBS). The default follows available CPU and memory, capped at 8.
    --test-path <PATH>Add a user-test file or directory to the same compile-once suite. Repeatable; overlapping paths are deduplicated.
    --affected-from <GIT_REF>Run only user-test files affected since a Git ref. Uses Harn's resolved module graph and falls back to the complete suite for any unmodelled change. One-shot user suites only.
    --planWith --affected-from, print a versioned JSON plan and exit without running tests. The plan reports selected or full, the reason, and the exact test files, so CI can size its execution matrix without weakening Harn's fallback policy.
    --watchRe-run tests on file changes (mutually exclusive with --junit / --json-out)
    --verbose / -vShow per-test timing and detailed failures, including a passing test's captured log/print/println output (a failing test's captured output is always shown)
    --timingShow detailed per-test timing, slowest tests/files, and phase totals. Every user-test run prints the concise p50/p90 latency line
    --junit <path>Write JUnit XML report for user tests or conformance; missing or unwritable destinations fail loudly. A failing user test's captured output is included as <system-out>
    --timeout <ms>Per-test timeout in milliseconds (default: 30000). For user suites, setup and shared import-graph compilation are measured separately and do not consume the pipeline-execution budget; other targets bound their test case or subprocess
    --approve-risky <operation>Explicitly authorize one exact risky stdlib operation for user-test execution; repeatable (for example git.push)
    --trusted-host-dispatchCompile the test and its private import graph for a Rust-host-selected route boundary, exposing privileged wire builtins such as host_call. Ordinary tests remain unprivileged.
    --max-test-ms <ms>Fail a passing test whose total setup + execution wall time exceeds the budget; forces a single measurement worker
    --max-execute-ms <ms>Fail a passing test whose measured execution phase exceeds the performance budget; forces a single measurement worker
    --timing-environment <name>Stamp a stable enforcing-environment identity into a user-test JSON receipt; also read from HARN_TEST_TIMING_ENVIRONMENT
    --timing-baseline <path>Read shard weights and absolute-cost baselines from a schema-v4 Harn user-test receipt. Requires the same --timing-environment; stale deleted or renamed cases fail the run.
    --max-cost-regression-percent <percent>Fail a case whose execution cost grows beyond this percentage of its receipt baseline (default: 25)
    --recordRecord LLM responses to .harn-fixtures/
    --replayReplay recorded LLM responses
    --coveragePrint per-file line coverage for executed Harn source (user test suites only)
    --coverage-out <path>Write an LCOV tracefile (implies --coverage); consumable by Codecov, genhtml, and Coverage Gutters
    --allow-emptyAccept a conformance run that executed no tests. Without it, a run that executed nothing exits non-zero.

    When no path is given, harn test auto-discovers a tests/ directory in the current folder. Conformance targets must resolve to a file or directory inside conformance/; the CLI now errors instead of silently falling back to the full suite when a requested target is missing.

    A conformance run that executed no tests exits non-zero. An all-zero result satisfies "nothing failed", so without this check the exit status cannot distinguish a selection that matched nothing from a suite that fully passed, and any caller gating on the exit code reads the vacuous run as green. The diagnosis names the selection, the filter, and how many selected files have no .expected, .error, or .lint sibling — the usual reason the runner can see a file but not run it. Pass --allow-empty where an empty selection is expected; parallel workers set it for themselves, because an individual shard may legitimately be empty and the parent applies the check to the aggregate.

    For timing-aware CI shards, first write an unsharded receipt in the enforcing environment, then give every sibling shard that immutable receipt:

    harn test tests/ --json-out ci-timings.json --timing-environment github-linux-x64
    harn test tests/ --shard-index 1 --shard-total 4 \
      --timing-baseline ci-timings.json --timing-environment github-linux-x64 \
      --json-out shard-1.json
    

    Harn validates that the baseline is its own current receipt format and that the environment identities match. Known cases use longest-processing-time bin packing; new cases fall back to count balancing. A case heavier than the rest of the suite receives its own shard and appears as dominant_case in the shard plan. Rebalancing never masks a slowdown: regressions fail the affected case and are also listed under cost_regressions in the same JSON report. Named std/timing spans inside a case are copied to that case's timing_spans array for sub-operation attribution. Shard weights and regressions use the receipt's phases.execute_ms, excluding cold setup and compile overhead.

    harn test-bench#

    Run a .harn script under a hermetic testbench: a paused mock clock, optional LLM fixtures, an optional copy-on-write filesystem overlay, optional subprocess tapes, and a deny-by-default network policy. The determinism controls make runs byte-reproducible so tapes can be recorded once and replayed as regression fixtures.

    harn test-bench run script.harn                          # paused clock, deny-all network
    harn test-bench run script.harn --llm-record fixture.jsonl
    harn test-bench run script.harn --process-record tape.bin --emit-tape run.tape
    harn test-bench run script.harn --fs-overlay . --emit-diff writes.diff
    harn test-bench replay script.harn --process-tape tape.bin
    harn test-bench fidelity recorded.tape replay.tape --mode byte-identical
    harn test-bench fidelity script.harn --against recorded.tape
    harn test-bench validate-annotations --tape run.tape run.tape.annotations.jsonl
    harn test-bench export-annotations run.tape.annotations.jsonl --kind friction
    

    harn test-bench run#

    Execute a script under the testbench. Selected flags:

    FlagDescription
    --clock <paused|real>Pin the mock clock (default paused) or use the real clock
    --start-at <UNIX_MS>Pin the paused clock to a specific UNIX-epoch millisecond value
    --llm-fixture <path> / --llm-record <path>Replay LLM responses from, or record them to, a JSONL fixture (mutually exclusive)
    --hypothesis-scenario <name>Install a trusted deterministic hypothesis native-adapter scenario; see testbench mode
    --fs-overlay <dir>Mount a copy-on-write overlay rooted at a worktree; writes stay in memory until the run ends
    --process-record <path> / --process-replay <path>Record subprocess invocations to a tape, or replay them from one (mutually exclusive)
    --process-wasi <dir>Resolve subprocess programs to <dir>/<program>.wasm under wasmtime (requires the testbench-wasi build feature)
    --network <deny|real>Outbound policy (default deny); --allow-host <host_or_cidr> (repeatable) opens specific destinations
    --emit-diff <path>Write a unified diff of overlay filesystem writes (requires --fs-overlay)
    --emit-tape <path>Write a unified event tape (clock reads, sleeps, LLM calls, FS writes, subprocess spawns)
    --runtime <paused-tokio|des>Runtime mode; des coalesces all work onto one thread for bit-exact tape replays

    Pass positional script arguments after --: harn test-bench run script.harn -- a b c.

    harn test-bench replay#

    Replay a previously recorded subprocess tape against a script and assert the run requests the same (program, args, cwd) tuples in the same order. Accepts --process-tape, plus the run-side --llm-fixture, --fs-overlay, --emit-tape, and --annotations flags.

    harn test-bench fidelity#

    Score replay fidelity. Pass two recorded tapes to diff them, or pass --against <tape> <script> to re-run the script and compare the fresh tape against the recorded one. --mode selects byte-identical (default), semantic, outcome, or phase-aware comparison.

    harn test-bench validate-annotations#

    Validate an annotation sidecar (<tape>.annotations.jsonl) against its target tape. Surfaces schema errors, unknown event_id references, and digest drift; exits non-zero (status 2) when any problems are found.

    harn test-bench export-annotations#

    Export annotations filtered by --kind (e.g. friction, crystallize_here, note; repeatable). --format jsonl (default) emits one annotation per line; --format friction re-emits matching annotations as FrictionEvent JSON for the friction roll-up consumer.

    harn repl#

    Start an interactive REPL with syntax highlighting, multiline editing, live builtin completion, and persistent history in ~/.harn/repl_history.

    harn repl
    

    The REPL keeps incomplete blocks open until braces, brackets, parentheses, and quoted strings are balanced, so you can paste or type multi-line pipelines and control-flow blocks directly.

    harn chat#

    Talk to a model and see how fast it answers.

    harn chat
    

    By default this talks to whatever local model harn local switch last selected, falling back to the configured provider when there is no selection. Name a model to talk to another one; the catalog resolves aliases and picks the provider.

    The session prints the route it resolved — model, provider, and endpoint — before the first prompt, so you always know which model your timings describe. If you name a model or provider explicitly and resolution lands somewhere else, the command refuses to start rather than talking to something you did not ask for.

    Which model gets picked#

    In order, first match wins:

    1. the MODEL argument and --provider flag
    2. the active harn local switch selection
    3. HARN_LLM_PROVIDER / HARN_LLM_MODEL
    4. the configured default provider and its default model

    Note that the stored selection deliberately outranks the environment variables, which is the opposite of what most commands do. harn chat exists to talk to the model you currently have running, and harn local switch is how you say which one that is; an exported HARN_LLM_MODEL from some earlier task should not quietly redirect it. Name the model as an argument to override both.

    Changing the selection does not affect any other command: nothing else reads it, so harn run and harn try keep resolving their own routing from the environment and the catalog.

    harn chat qwen36-coder
    harn chat --provider ollama --system "Answer in one sentence."
    

    Each turn streams tokens as they arrive and then reports one speed line on stderr:

    49.31 tok/s · 143 tokens · first token 210ms · 3.1s total
    

    Use --verbose for the full breakdown (load, prompt eval, eval counts, durations, and rates) or --no-stats for none. Rates come from the server's own timings when the provider reports them, so they measure the model rather than the round trip; a measurement the provider did not send is left out rather than shown as zero.

    This is a plain-completions surface, not an agent: no tools, and no system prompt unless you set one. That keeps a speed reading about the model instead of about scaffolding it never asked for.

    Commands inside the session:

    CommandEffect
    /help, /?list the commands
    /byeexit (Ctrl-D also works)
    /model [name]show the current model, or switch
    /system [text]show the system prompt, or set it
    /stats [off|on|verbose]change the speed line
    /clearforget the conversation so far

    Wrap a message in """ to send several lines at once.

    harn bench#

    Benchmark a .harn file over repeated runs, or score deterministic replay fixtures.

    harn bench main.harn
    harn bench main.harn --iterations 25
    harn bench main.harn --iterations 25 --profile-json bench.json
    harn bench portable reducer.harn --entry reduce --input event.json --threads 4 --json
    harn bench replay --json --output replay-benchmark.json
    harn bench replay conformance/replay-oracle/fixtures --filter approval --json
    

    harn bench parses and compiles the file once, executes it with a fresh VM for each iteration, and reports wall time plus aggregated LLM token, call, and cost metrics. The wall-time rollup includes min, mean, p50, p95, max, stddev, and total. --profile prints the aggregate categorical timing rollup; --profile-json writes a JSON report with iterations[], mean_ms, p50_ms, p95_ms, stddev_ms, and rollup. HARN_PROFILE=1 and HARN_PROFILE_JSON=<path> work as environment aliases.

    harn bench portable measures the canonical portable compiler, artifact decoder, and native kernel dispatch as separate sample sets. It accepts a function or pipeline entry, one JSON input value, 1 through 100,000 repeated compile/decode samples, 1 through 1,000,000 dispatch samples, and 1 through 256 native operating-system workers. The command records the cold first compile separately from repeated compilation. Workers share the immutable program artifact but never execution state; they rendezvous before the timed batch, and every terminal value must match the first result. The receipt reports both per-dispatch latency statistics and batch wall time/throughput.

    --json prints and --output <path> writes the cross-target harn.portable_kernel.benchmark.v1 receipt. It includes artifact and terminal digests plus Harn/kernel versions, artifact format and semantic/opcode ABI fingerprints, build profile, OS, and architecture. Portable benchmarking is host-side measurement: it neither grants a clock to the program nor produces full-VM profile spans. Consequently, --profile, --profile-json, HARN_PROFILE, and HARN_PROFILE_JSON are rejected for this subcommand; use the versioned receipt or harn bench <file> --profile instead. See Benchmark the portable kernel for a reproducible native/browser comparison and the relationship to harn time and std/timing.

    harn bench replay reads bench/replay/suite.json by default. It emits schema harn.replay_benchmark.report.v1, including cloud-platform ingest metadata, fixture receipts, replay-fidelity scores, permission-preservation scores, tool-call drift counts, transcript drift counts, observed interaction totals, and first-divergence triage data. Use --output <path> to write the JSON report and --json to print it to stdout. The command exits non-zero when any fixture fails its expected replay result.

    External trace pairs can be adapted with the documented OpenCode-inspired JSONL adapter:

    harn bench replay \
      --adapter opencode-jsonl \
      --external-first bench/replay/adapters/opencode/first.jsonl \
      --external-second bench/replay/adapters/opencode/second.jsonl \
      --external-name opencode-permission-run \
      --json
    

    harn portable#

    Compile Harn sources into a portable artifact and execute it through the same kernel that browser Workers and embedding hosts use. See the Portable Kernel reference for the artifact format, the source-package manifest, and the grant contract.

    harn portable package reducer.harn --output reducer.package.json
    harn portable compile reducer.harn --entry reduce --output reducer.hbc
    harn portable start reducer.hbc \
      --input event.json \
      --grants grants.json \
      --snapshot-out reducer.snapshot
    harn portable resume reducer.hbc \
      --snapshot reducer.snapshot \
      --result capability-result.json \
      --grants grants.json \
      --snapshot-out next.snapshot
    

    package resolves the filesystem import graph once and writes the data-only manifest that hosts without filesystem access compile from; --check fails when a checked-in projection is stale. compile writes the deterministic artifact, defaulting to the main function; pass --entry-kind pipeline to name a pipeline instead.

    start and resume emit the canonical Harn JSON envelope with a status of completed, suspended, or failed. A suspended transition writes its opaque snapshot to --snapshot-out, and resume feeds one typed capability result back in. Omit --grants for pure execution.

    harn time#

    Wrap a subcommand with phase-level wall-clock timing plus per-LLM-call and per-tool-call latency. Today only harn time run is supported.

    harn time run main.harn
    harn time run main.harn --json
    harn time run main.harn --json --no-cache
    harn time run main.harn --no-sandbox
    harn time run main.harn --eager-project-handlers
    harn time run main.harn --write-root /path/to/output
    harn time run main.harn --grant fireworks=env:FIREWORKS_API_KEY,expose=FIREWORKS_API_KEY
    harn time run -e 'harness.stdio.log("hi")' --json
    harn time run script.harn -- arg1 arg2
    

    harn time run accepts the same confinement and credential flags as harn run — the two share one flag definition, so a run and its timed twin are always confined identically.

    Pass --eager-project-handlers when the measurement should include fail-fast project validation. It implies project-trigger registration and loads every trigger and hook handler module before the timed script runs. Without the flag, harn time run keeps the ordinary demand-driven behavior and resolves a policy hook only when its matching event fires.

    The output is keyed by seven fixed rows — parse, typecheck, bytecode_compile, run_setup, run_main, module_compile, and module_load — even when a bytecode-cache hit lets us skip parse/typecheck. The bytecode_compile row carries a cache: "hit" | "miss" field so cost-and-perf eyeballs and agent pipelines can dispatch on cache state without a separate flag. Rows carry kind: "top_level" | "attribution"; only top-level rows reconcile wall time. module_compile and module_load attribute work that overlaps run_setup and run_main; do not add them to the five top-level rows. Their events values count successful compiles and unique fresh-VM loads.

    --json emits a versioned JsonEnvelope (schema registered as time run in harn --json-schemas):

    {
      "schemaVersion": 2,
      "ok": true,
      "data": {
        "command": "run",
        "target": "main.harn",
        "phases": [
          { "name": "parse", "kind": "top_level", "duration_ms": 12, "input_bytes": 4096 },
          { "name": "typecheck", "kind": "top_level", "duration_ms": 80 },
          { "name": "bytecode_compile", "kind": "top_level", "duration_ms": 35, "cache": "miss" },
          { "name": "run_setup", "kind": "top_level", "duration_ms": 8 },
          { "name": "run_main", "kind": "top_level", "duration_ms": 1200, "events": 14 },
          { "name": "module_compile", "kind": "attribution", "duration_ms": 40, "events": 3 },
          { "name": "module_load", "kind": "attribution", "duration_ms": 75, "events": 8 }
        ],
        "llm_calls": [
          { "model": "claude-sonnet-5", "latency_ms": 850, "tokens": 1500 }
        ],
        "tool_calls": [{ "name": "mcp_call", "latency_ms": 200 }],
        "totals": {
          "wall_ms": 1335,
          "cpu_ms": 320,
          "cache_hits": 0,
          "cache_misses": 1
        },
        "exit_code": 0
      }
    }
    

    In --json mode the wrapper hands stdout to the envelope: any script output is redirected to stderr so harn time run … --json | jq … works without filtering. Diagnostics and non-zero exit codes from the wrapped run still propagate through harn time. --no-cache sets HARN_BYTECODE_CACHE=0 for the duration of the invocation only, forcing a cold parse/typecheck/compile. --no-sandbox is forwarded to the wrapped harn run path.

    harn viz#

    Render a .harn file as a Mermaid flowchart.

    harn viz main.harn
    harn viz main.harn --output docs/graph.mmd
    

    harn viz parses the file, walks the AST, and emits a Mermaid flowchart TD graph showing pipelines, functions, branches, loops, and other workflow-shaped control-flow nodes.

    harn flow#

    Inspect and operate the Flow shipping substrate.

    harn flow replay-audit --store .harn/flow.sqlite --predicate-root . --touched-dir crates/harn-vm
    harn flow replay-audit --since 2026-04-26 --fail-on-drift --json
    harn flow ship watch --store .harn/flow.sqlite --predicate-root . \
      --touched-dir crates/harn-vm --mock-pr-out .harn/flow/mock-pr.json --json
    harn flow archivist scan . --out .harn/flow/archivist-proposals.json --json
    

    replay-audit compares predicate hashes pinned in derived slices with the current invariants.harn predicate set. Drift is advisory unless --fail-on-drift is present.

    ship watch is the Phase 0 Ship Captain shadow-mode surface. It groups stored atoms into intent summaries, derives a candidate slice, discovers applicable invariants.harn predicates for the touched directories, persists a local shipping receipt, and can write a mock PR receipt without touching a remote GitHub repository.

    archivist scan emits review-ready predicate proposal metadata from the repo's stack hints and existing Flow predicates. It is propose-only; it does not edit invariants.harn.

    harn persona#

    List, inspect, and control durable agent persona manifests from harn.toml.

    harn persona list
    harn persona list --json
    harn persona inspect merge_captain
    harn persona inspect merge_captain --json
    harn persona activate agents/reviewer --autonomy-tier suggest --json
    harn persona activations --json
    harn persona deactivate agents/reviewer --json
    harn persona --manifest examples/personas/harn.toml inspect merge_captain --json
    harn persona --manifest examples/personas/harn.toml status merge_captain --json
    harn persona --manifest examples/personas/harn.toml tick merge_captain --json
    harn persona --manifest examples/personas/harn.toml trigger merge_captain \
      --provider github --kind pull_request \
      --metadata repository=burin-labs/harn --metadata number=462 --json
    harn persona --manifest examples/personas/harn.toml pause merge_captain
    harn persona --manifest examples/personas/harn.toml resume merge_captain
    harn persona --manifest examples/personas/harn.toml disable merge_captain
    harn persona --manifest examples/personas/harn.toml supervision tail --json
    harn persona --manifest examples/personas/harn.toml supervision tail \
      --persona merge_captain --since-event-id 42 --follow --limit 200 --json
    
    FlagDescription
    --manifest <path>Use an explicit harn.toml path or directory containing one
    --state-dir <dir>Store persona runtime events under a durable EventLog base directory, default .harn/personas; installed-persona activations always remain project-scoped beside the root manifest
    --jsonEmit stable JSON for list, inspect, status, controls, trigger, tick, and budget receipts. supervision tail always emits NDJSON frames and accepts --json for host symmetry
    --persona <name>Filter supervision tail to one persona; omitted streams every persona in the local state directory
    --since-event-id <N>Replay supervision tail frames with event_id > N
    --followKeep supervision tail open and stream new appended frames
    --limit <N>Cap emitted supervision tail frames

    harn persona validates the manifest before printing. It rejects missing entry workflows, unknown capabilities, invalid budget fields, invalid schedules, and handoffs that point at undeclared personas.

    Installed package personas are addressed as <package-alias>/<persona-name>. activate writes an atomic project record that pins package and policy digests; its optional autonomy, tool, and capability flags may only reduce runtime authority. Budget, model, and receipt policies remain inherited and are pinned as part of the full exported persona contract; package permissions and host requirements remain install-time contracts. activations lists those records and deactivate removes one even after its package has been removed. Package installation alone never activates a persona.

    Runtime commands append event-sourced lifecycle records to persona.runtime.events. pause queues matching trigger events, resume drains queued events once under a lease, and disable records later events as dead-lettered. tick, trigger, and spend enforce per-persona daily, hourly, run, and token budgets before recording expensive-work receipts. supervision tail projects those records into hosted-compatible persona/update NDJSON frames with event_id, persona_id, persona_kind, optional persona_version, actor, update_kind, occurred_at, and payload.

    harn pg codegen#

    Generate Harn record types from a directory of .sql migrations so Postgres query results can be type-checked without a live database.

    harn pg codegen --dir migrations
    harn pg codegen --dir migrations --out src/db_types.harn
    harn pg codegen --dir migrations --out src/db_types.harn --check
    harn pg codegen --dir migrations --out src/db_types.harn --suffix Record
    

    Replays every forward migration (.sql, excluding .down.sql) in lexicographic order — the same discovery rule pg_migrate uses — applying CREATE TABLE, ALTER TABLE … ADD/DROP/ALTER/RENAME COLUMN, RENAME TO, and DROP TABLE, then emits one type <Table>Row = {…} per table mirroring the live schema. Prints to stdout unless --out is given. --check (requires --out) verifies the file is current without writing and exits non-zero on drift — wire it into CI. See Postgres → Typed rows from migrations.

    harn fmt#

    Format .harn source files. Accepts files or directories.

    harn fmt main.harn
    harn fmt src/
    harn fmt --check main.harn            # check mode (no changes, exit 1 if unformatted)
    harn fmt --line-width 80 main.harn    # custom line width
    harn fmt --separator-width 60 main.harn # fixed section-header separator width
    
    FlagDescription
    --checkCheck mode: exit 1 if any file would be reformatted, make no changes
    --line-width <N>Maximum line width before wrapping (default: 100)
    --separator-width <N>Fixed total width for normalized // ---- section-header separators (default: line-width minus indent)

    The formatter enforces a 100-character line width by default (overridable with --line-width). When a line exceeds this limit the formatter wraps it automatically:

    harn fmt also normalizes optional semicolon-separated statements back to the canonical newline-separated style. Semicolons are accepted as input syntax in statement-list contexts, but they are not preserved in formatter output.

    • Comma-separated forms — function call arguments, function declaration parameters, list literals, dict literals, struct construction fields, enum constructor payloads, selective import names, interface method parameters, and enum variant fields all wrap with one item per line and trailing commas.
    • Binary operator chains — long expressions like a + b + c + d break before the operator. Operators that the parser cannot resume across a bare newline (-, in, not in) get an automatic backslash continuation (\); other operators (+, *, /, %, ||, &&, |>, ==, !=, <, >, <=, >=, ??) break without one.
    • Operator precedence parentheses — the formatter inserts parentheses to preserve semantics when the AST drops them (e.g. a * (b + c) stays parenthesised) and for clarity when mixing && / || (e.g. a && b || c becomes (a && b) || c) or when ?? is nested inside looser binary expressions (e.g. x == y ?? false becomes x == (y ?? false)).

    Trailing comments and line width#

    A trailing (same-line) comment is treated as an unbreakable token that does not count toward the line width. This matches rustfmt, Prettier, and gofmt, and is the least-surprising behaviour:

    • The formatter never relocates a trailing comment to its own line, and never reflows code to make room for one. If let x = compute() // why exceeds the width because of the comment, the line is simply left long.
    • Code still wraps on its own merits. The wrap decision is based on the code alone; the comment is appended afterward, riding the last physical line of a wrapped construct (e.g. the closing ] of a wrapped list literal).

    There is no separate "line too long" lint — width is a formatter concern, so a trailing comment that overflows is never reported as a diagnostic.

    After formatting, harn fmt verifies the complete emitted output as a final guard. If a breakable line still exceeds the configured width, the command reports the offending line and leaves the file unchanged; only a single unbreakable token or an overflowing comment may remain longer.

    harn lint#

    Lint one or more .harn files or directories for common issues (unused variables, unused functions, dead code after terminating statements, pointless comparisons, redundant clones, empty blocks, missing /** */ HarnDoc on public functions, etc.).

    harn lint also walks .harn.prompt (and bare .prompt) files in the same pass and applies template-specific drift-prevention rules (template-provider-identity-branch, template-variant-explosion — see docs/src/prompt-templating.md). Project lint rules are discovered from [rules] in harn.toml: declarative language = "harn" TOML rules and *.lint.harn script rules use ruleDirs, while trusted native dynamic libraries use nativeRuleDirs.

    harn lint main.harn
    harn lint src/ tests/
    harn lint prompts/system.harn.prompt
    

    To ratchet strict linting onto newly added Harn source lines without making inherited warnings block unrelated work, let harn lint derive its source scope from two Git revisions:

    harn lint --strict --changed-from origin/main
    harn lint --strict --changed-from origin/main --changed-to HEAD
    

    --changed-to defaults to HEAD. Changed-line mode accepts no explicit path targets and cannot be combined with --fix. It resolves both revisions to commits, detects renames, and evaluates added physical line ranges in tracked .harn and .harn.txt files. Warning and error diagnostics are emitted only when their complete UTF-8 byte span overlaps an added line; information diagnostics remain visible in --json reports. Deleted, empty, and rename-only files are recorded but do not create lint targets. Git failures, unsafe paths, source contents that differ from the evaluated target revision, unreadable sources, and missing or invalid warning/error spans fail closed.

    Output streams#

    harn lint, harn check, and harn fmt write every human-readable line to stderr — findings, the no issues found line, and the applied-fix and formatted-file lines alike. stdout carries machine-readable output only, which is the --json envelope and nothing else.

    The consequence worth knowing before you write a sweep: 2>/dev/null silences these commands rather than trimming them, and harn lint src/ > findings.txt leaves the file empty. Redirect stderr, or use --json.

    harn lint src/ 2> findings.txt
    harn lint --json src/ | jq '.data.files[].diagnostics'
    

    harn lint --json emits the schema-v1 envelope documented in the CLI --json contract. Discover the complete inline schema with harn --json-schemas --command lint, and decode it in Harn through std/cli/envelope. Diagnostic span values are UTF-8 half-open byte offsets [start, end).

    --trusted-host-dispatch lints the given files as privileged artifacts, so a call to a builtin declared privileged_wirehost_call and its kin — is sanctioned rather than a HARN-LNT-072 finding. Use it when the host that serves those operations from its own bridge is the one linting them; ordinary source stays unprivileged. The flag mirrors harn check --trusted-host-dispatch and, like it, monotonically enables [check] trusted_host_dispatch. Declaring the key in harn.toml is usually better than passing the flag at every call site, because it governs checking, linting, tests, precompilation, direct execution, and file-backed MCP, A2A, site, and ACP execution together:

    [check]
    trusted_host_dispatch = true
    

    Only the privileged_wire finding is suppressed. The declaration says who may reach a wire, not that a runtime internal became source-visible, so a runtime_internal builtin and a capability method called as a bare global both keep reporting.

    Pass --fix to automatically apply safe fixes (e.g., varlet for never-reassigned bindings, boolean comparison simplification, unused import removal, string interpolation conversion, let-then-return simplification, and removing redundant clone()/to_string(...)/to_int(...)/to_list(...) wrappers or unnecessary parentheses around single values):

    harn lint --fix main.harn
    

    The Harn LSP also exposes these autofixes as quick-fix code actions (Cmd+./Ctrl+. in most editors) and as a bulk source.fixAll.harn action that VS Code can run on save:

    "[harn]": {
      "editor.codeActionsOnSave": { "source.fixAll.harn": "always" }
    }
    

    harn fix#

    Emit a repair plan for one .harn file or directory, or apply clean machine-applicable repairs under an explicit safety ceiling. Plan mode runs the same type-check, lint, and preflight diagnostic passes used by harn check, keeps diagnostics that carry a registered repair classifier, and reports the concrete FixEdit edits when a diagnostic already has a machine-applicable fix.

    harn fix --plan main.harn
    harn fix --plan --json main.harn
    harn fix --plan --json --safety behavior-preserving src/
    harn fix --apply --safety behavior-preserving main.harn
    harn fix --apply --dry-run --json --safety scope-local src/
    harn fix --apply --safety surface-changing src/
    harn fix --apply --safety surface-changing --code HARN-TYP-028 src/
    harn fix --apply --safety behavior-preserving --preserve-implicit-any --json src/
    harn fix --apply --safety surface-changing --code HARN-LNT-073 src/
    

    --code <CODE> restricts the run to one diagnostic code and may be repeated; omitting it keeps every code. Use it when a migration should touch one class of finding rather than everything at its safety ceiling. Narrowing happens in the plan, so --plan --code <CODE> shows exactly what --apply --code <CODE> writes.

    HARN-TYP-028 marks declared parameters that have no type annotation. Its surface-changing repair infers parameter types from body usage and call sites, then rechecks each edited file. The JSON plan reports parameterAnnotations with inferred and unresolved counts plus cause clusters. Unresolved parameters receive explicit unknown, preserving type checks at every use site until a human supplies a narrower contract. Review that residue before applying a large migration.

    --preserve-implicit-any is the unattended version-migration mode for source written before HARN-TYP-028. It translates every formerly unannotated declared parameter to explicit any; it never infers a narrower type or writes unknown. Its JSON census names every scanned file and observed parameter, plus changed, pending, unresolved, and changed-semantics names and counts. Completion requires zero pending and failure counts. The command exits nonzero if ordinary source cannot be read or parsed, an observed parameter remains unannotated, or the audited result is anything other than any. Intentionally invalid fixtures with a sibling .error declaration are listed under excludedFiles. Use the ordinary --code HARN-TYP-028 surface-changing repair when authoring a narrower contract.

    Ambient-capability migrations add an explicit harness: Harness parameter and update callers. This is a surface-changing repair because Harn has no ambient root harness. Follow the repair with harn lint; the capability-attenuation lint recommends replacing broad root parameters with the narrowest coherent nominal handle a helper actually needs.

    --json returns a RepairPlan with schemaVersion: 2, diagnostics[], repairs[], skippedFiles[], and safetyLevels[]. Each repair includes the diagnostic code, repair {id, summary, safety}, impact metadata, candidate edits, applies_cleanly, and conflicts_with indexes for overlapping edit ranges. Ambient Harness repairs classify local rewrites separately from public signature changes and flag repairs that require cross-module caller updates. Directory runs continue after read, lex, or parse failures; those files appear in skippedFiles[] with reason and diagnostic details, and the command exits nonzero after processing the remaining parseable files. Apply mode requires --safety <format-only|behavior-preserving|scope-local|surface-changing|capability-changing>; needs-human repairs are propose-only and are never auto-applied. --apply --json returns schemaVersion, applied[], skipped[], skippedFiles[], and post_apply_diagnostics_count. --dry-run reports the same apply set without writing files.

    Apply mode preserves a file's formatting state rather than imposing one. A repair changes line lengths, so a file that was in canonical harn fmt form is formatted again after the edit and stays canonical; a file that was not is returned exactly as its author keeps it, so the diff stays the size of the repair.

    harn check#

    Type-check one or more .harn files or directories and run preflight validation without executing them. The preflight pass resolves imports, checks literal harness.fs.render_prompt(...) targets, detects import symbol collisions across modules, validates privileged provenance-stamped host_call("capability.operation", ...) bridge contracts, and flags missing template resources, execution directories, and worker repos that would otherwise fail only at runtime. Source-aware lint rules run as part of check. (The missing-harndoc warning for undocumented pub fn APIs is opt-in via [lint] require_docstrings = true in harn.toml.)

    check builds a cross-module graph from each entry file and follows import statements recursively. When every import in a file resolves, the typechecker knows the exact set of names that module brings into scope and will emit a hard error for any call target that is neither a builtin, a local declaration, a struct constructor, a callable variable, nor an imported symbol:

    error: call target `helpr` is not defined or imported
    

    This catches typos and stale imports before the VM runs. If any import in the file is unresolved, the stricter check is turned off for that file so one broken import does not avalanche into spurious errors — the unresolved import itself still fails at runtime.

    harn check main.harn
    harn check src/ tests/
    harn check --host-capabilities host-capabilities.json main.harn
    harn check --trusted-host-dispatch --host-capabilities host-capabilities.json routes/
    harn check --bundle-root .bundle main.harn
    harn check --invariants main.harn
    harn check --strict --strict-types src/
    harn check --workspace
    harn check --preflight warning src/
    
    FlagDescription
    --host-capabilities <file>Load a host capability manifest for preflight validation. Supports plain {capability: [ops...]} objects, nested {capabilities: ...} wrappers, and per-op metadata dictionaries with optional param_discriminators literal sets. Discriminator policies may explicitly allow a dynamic forwarding boundary when the host verifies it separately. Overrides [check].host_capabilities_path in harn.toml.
    --trusted-host-dispatchCheck an embedder-owned route module graph with privileged host builtins such as host_call visible. Use only when a Rust host selects and loads the graph through the trusted-dispatch VM boundary; ordinary modules remain unprivileged. Monotonically enables [check].trusted_host_dispatch. Reaches the lint rules harn check runs too, so the same call does not come back as a HARN-LNT-072 warning.
    --bundle-root <dir>Validate harness.fs.render_prompt(...), and template paths against an alternate bundled layout root
    --invariantsEvaluate @invariant(...) annotations on functions, tools, and pipelines. Violations fail the check and are reported as invariant[<name>] diagnostics with concrete source spans.
    --workspaceWalk every path listed in [workspace].pipelines of the nearest harn.toml. Positional targets remain additive.
    --preflight <severity>Override preflight diagnostic severity: error (default, fails the check), warning (reports but does not fail), or off (suppresses all preflight diagnostics). Overrides [check].preflight_severity.
    --strictTreat every warning as a failure after rendering all files. Monotonically enables [check].strict; it never disables workspace strictness.
    --strict-typesFail on unvalidated boundary-API values used in field or subscript access.

    Files are checked on a parallel worker pool sized to the machine's available cores, and resolved-module parsing is memoized for the whole run, so whole-tree checks scale with the number of distinct modules rather than files x import closure. Per-file output order and content match the serial driver exactly. Set HARN_CHECK_JOBS=<n> to pin the pool size (HARN_CHECK_JOBS=1 restores fully serial checking, e.g. when bisecting a diagnostic that depends on machine load). The module-graph build that precedes checking is likewise parallel per import wave; pin it independently with HARN_MODULE_GRAPH_JOBS=<n>.

    Check results also persist across runs: each file's diagnostics are cached under the shared Harn cache directory (HARN_CACHE_DIR, alongside the bytecode cache), keyed by the file's content, its transitive import closure, the effective [check] config plus CLI overrides, and the CLI build itself. Preflight's filesystem lookups — template and prompt-asset paths, directory targets — are recorded with each cached result and revalidated on every hit, so creating, editing, or deleting a referenced file invalidates the cache even though that file is not part of the import graph. A warm re-check of an unchanged tree replays byte-identical output without re-running the typechecker or linter. Set HARN_CHECK_RESULT_CACHE=0 to disable just this cache; HARN_BYTECODE_CACHE=0 disables it together with the bytecode cache.

    --invariants includes the capability-policy lattice:

    @invariant("capability.policy",
      allow: "fs.write,process.exec,mcp.connector,llm.model",
      workspace: "src/**",
      require_approval: "fs.write",
      require_command_policy: "process.exec",
      require_egress_policy: "mcp.connector",
      require_budget: "llm.model",
    )
    fn release_worker(harness: Harness, client) {
      const _approval = harness.interaction.request_approval(
        "edit",
        {capabilities_requested: ["fs.write"]},
      )
      harness.fs.write_text("src/release.md", "ready")
      harness.runtime.with_command_policy(
        {deny: ["rm"]},
        { -> harness.process.exec("git status") },
      )
      harness.net.egress_policy({default: "deny", allow: ["api.github.com"]})
      harness.tools.mcp_call(client, "github.search", {query: "harn"})
      harness.llm.call("summarize", nil, {budget: {max_output_tokens: 128}})
    }
    

    Canonical capability names are fs.write, process.exec, network.access, mcp.connector, llm.model, worker.dispatch, human.approval, and autonomy.policy.

    harn.toml — [check] and [workspace] sections#

    harn check walks upward from the target file (stopping at the first .git directory) to find the nearest harn.toml. The following keys are honored:

    [check]
    # Load an external capability manifest. Path is resolved relative to
    # harn.toml. Accepts JSON or TOML with the namespaced shape
    # { workspace = [...], process = [...], project = [...], ... }.
    host_capabilities_path = "./schemas/host-capabilities.json"
    host_served_capabilities_path = "./schemas/host-served-capabilities.json"
    runtime_installed_host_operations = ["project.control_plane_handler"]
    require_declared_operations_served = false
    
    # The served file uses the same JSON or TOML shape as the declared file.
    # Every declared operation must appear there unless its exact name is listed
    # as runtime-installed. Wildcards are not allowed in that list.
    # ACP warns when its connected host does not serve a declaration. Set
    # require_declared_operations_served to true to stop before the script runs.
    
    # Or declare inline:
    [check.host_capabilities]
    project = ["ensure_enriched", "enrich"]
    workspace = ["read_text", "write_text"]
    
    [check]
    # Treat warnings from any check phase as failures. The one-shot equivalent is
    # `harn check --strict`; the CLI flag can enable but never disable this setting.
    strict = true
    
    # Enable boundary-value type checks persistently. Combine the one-shot flags as
    # `harn check --strict --strict-types` for a zero-warning type-safety gate.
    strict_types = true
    
    # Check only embedder-owned route modules that a Rust host loads through the
    # trusted-dispatch VM boundary. The one-shot equivalent is
    # `harn check --trusted-host-dispatch`.
    trusted_host_dispatch = true
    
    # Downgrade preflight errors to warnings (or suppress entirely with "off").
    # Keeps type diagnostics visible while an external capability schema is
    # still catching up to a host's live surface.
    preflight_severity = "warning"
    
    # Suppress preflight diagnostics for specific capabilities/operations.
    # Entries match either an exact "capability.operation" pair, a
    # "capability.*" wildcard, a bare "capability" name, or a blanket "*".
    preflight_allow = ["mystery.*", "runtime.task"]
    
    [workspace]
    # Directories or files checked by `harn check --workspace`. Paths are
    # resolved relative to harn.toml.
    pipelines = ["pipelines", "scripts"]
    

    Preflight diagnostics are reported under the preflight category so they can be distinguished from type-checker errors in IDE output streams and CI log filters.

    harn canon#

    Evaluate harn-canon invariant packs against changed files. Packs are declared in a canon-packs.json manifest (resolved from --canon-root, HARN_CANON_ROOT, or the workspace .harn/canon directory) and route to the given paths by manifest path rules unless --pack pins them explicitly.

    harn canon check src/foo.harn src/bar.harn
    harn canon check src/ --include-semantic          # run semantic predicates too
    harn canon check src/ --pack style --pack safety   # bypass manifest routing
    harn canon check src/foo.harn --advisory --json    # never fail; emit JSON envelope
    

    harn canon check#

    Run a pack's deterministic predicates (and, with --include-semantic, its semantic predicates) over the evaluated slice and report blocking findings. Exits non-zero when harn-canon reports blocking findings unless --advisory is set.

    FlagDescription
    --canon-root <path>Directory containing canon-packs.json (default: HARN_CANON_ROOT or <workspace>/.harn/canon)
    --workspace-root <path> / --root <path>Root used to resolve relative PATH arguments (default: .)
    --pack <id> / --pack-id <id>Explicit pack id; repeat to bypass manifest path routing
    --include-missingInclude missing paths in the evaluated slice with empty text
    --include-semanticRun semantic predicates in addition to deterministic ones
    --budget-ms <ms>Per-pack predicate budget in milliseconds (default: 50)
    --advisoryPrint findings but exit zero even when harn-canon reports blocking findings
    --feedback-header <text>Header used for the human feedback text (default: harn-canon feedback)
    --jsonEmit a stable JSON envelope

    harn explain#

    harn explain dispatches on two forms behind one subcommand.

    Form 1 — explain a stable diagnostic code#

    harn explain HARN-TYP-014
    harn explain HARN-TYP-014 --json
    

    Pass any identifier registered in the in-binary diagnostic-code registry (HARN-<CAT>-<NNN> — see crates/harn-parser/src/diagnostic_codes.rs). Text mode prints the embedded markdown explanation plus a curated See also: list of related codes. --json emits a stable envelope an agent or editor can ingest without parsing prose:

    {
      "schemaVersion": 1,
      "code": "HARN-OWN-001",
      "category": "OWN",
      "summary": "immutable binding is reassigned",
      "body": "# HARN-OWN-001 — ... full markdown ...",
      "repairs": [
        {
          "id": "bindings/make-mutable",
          "safety": "scope-local",
          "summary": "Mark the binding `mut` so it can be reassigned"
        }
      ],
      "related": [],
      "apiStability": "stable"
    }
    

    schemaVersion is the contract LSPs, IDEs, and hosted error-page mirrors dispatch on; bumps will follow a deprecation cycle. repairs carries the structured repair classifier — a namespaced kebab-case id plus a six-level safety class (format-onlybehavior-preservingscope-localsurface-changingcapability-changingneeds-human) — that agents use to decide whether to auto-apply, propose, or escalate a fix. Codes without a registered repair shape return "repairs": []. An unknown code exits with status 2.

    Form 2 — explain a control-flow invariant violation#

    harn explain --invariant fs.writes write_patch main.harn
    harn explain --invariant approval.reachability deploy_agent agent.harn
    harn explain --invariant budget.remaining spend_budget budget.harn
    

    This is the companion to harn check --invariants: check answers whether a handler violates its declared contract, and --invariant-mode explain shows the path that makes the violation reachable. It loads the file, rebuilds the same handler IR used by check, and prints:

    • the invariant name and handler
    • the violation message plus any help text
    • a numbered CFG path showing the source locations traversed to reach the violating call or assignment

    If the handler does not exist or does not declare the requested @invariant(...), the command exits nonzero with a direct error message.

    harn contracts#

    Export machine-readable contracts for hosts, release tooling, and embedded bundles.

    harn contracts builtins
    harn contracts host-capabilities --host-capabilities host-capabilities.json
    harn contracts bundle main.harn --verify
    harn contracts bundle src/ --bundle-root .bundle --host-capabilities host-capabilities.json
    

    harn contracts builtins#

    Print the parser/runtime builtin registry as JSON, including return-type hints and alignment status.

    harn contracts host-capabilities#

    Print the effective host-capability manifest used by preflight validation after merging the built-in defaults with any external manifest file.

    harn contracts bundle#

    Print a bundle manifest for one or more .harn targets. The manifest includes:

    • explicit entry_modules, import_modules, and module_dependencies edges
    • explicit prompt_assets and template_assets slices, plus a full assets table resolved through the same source-relative rules as harness.fs.render_prompt(...)
    • required host capabilities discovered from nominal Harness usage and privileged, provenance-stamped host_call(...) bridge sites
    • literal execution directories and worker worktree repos
    • a summary block with stable counts for packagers and release tooling

    Use --verify to run normal Harn preflight validation before emitting the bundle manifest and return a non-zero exit code if the selected targets are not bundle-safe.

    harn init#

    Scaffold a new project with harn.toml and main.harn.

    harn init              # create in current directory
    harn init my-project   # create in a new directory
    harn init --template eval
    

    harn new#

    Scaffold a new project from a starter template. Supported templates are basic, agent, chat, mcp-server, eval, pipeline-lab, package, and connector.

    harn new my-agent --template agent
    harn new my-chat --template chat
    harn new local-mcp --template mcp-server
    harn new eval-suite --template eval
    harn new package my-lib
    

    harn init and harn new share the same scaffolding engine. Use init for the default quick-start flow and new when you want the template choice to be explicit.

    The agent template creates an agent/ app layout with instructions.md, app.harn, local skills, and convention folders for tools, subagents, channels, sandbox policy, and schedules.

    harn tool#

    Scaffold Harn-native custom tool packages, execute a published registry as a generated CLI, or inspect its canonical catalog.

    harn tool new acme-echo
    harn tool new acme-echo --dir packages/acme-echo
    harn tool new acme-echo --description "Echo text for tests."
    harn tool run server.harn widgets get --widget-id 42 --json # harn-doc-cli: allow-stale
    harn tool run server.harn widgets get --help
    harn tool completions server.harn --shell zsh > _widgets
    harn tool schema server.harn --pretty
    harn tool schema library.harn --surface exports --pretty
    

    The generated package includes [[package.tools]] metadata, a stable tools export, an MCP entrypoint for lazy agent discovery, package-local dispatch tests, API docs, and a CI call to the canonical harn package verify contract. Run the entrypoint with harn serve mcp server.harn.

    harn tool run and harn serve mcp --surface script consume the same ToolRegistry and invoke the same handler closures. See Tool registry adapters for metadata, coercion, validation, and catalog details.

    When a tool declares error_schema, a matching thrown value exits nonzero. JSON output writes {"ok":false,"error":{"kind":"application","tool":...,"data":...}}; text output writes a generic application-error summary to stderr. Undeclared throws and values outside the declared schema remain runtime or contract failures.

    harn tool completions emits static completion for Bash, Zsh, Fish, or PowerShell. It reads the same validated command tree as harn tool run, so command aliases, flags, positionals, enum values, and path hints cannot drift from parsing or help. Use --shell bash, zsh, fish, or power-shell.

    harn tool schema emits the versioned harn-tools/2.0 catalog. Its --surface value is script or exports; script is the default. The exports surface compiles public functions and resolves their input, output, and declared throws types without running main or acquiring runtime capabilities. Use --pretty for indented JSON.

    harn skill#

    Every skill operation lives under one harn skill noun: corpus discovery and inspection, scaffolding, and provenance (signing, endorsement, verification, trust).

    # Corpus & discovery
    harn skill list                      # canonical embedded corpus
    harn skill get harn-language --full  # one skill's frontmatter (+ body)
    harn skill dump --all                # write the corpus to disk
    harn skill resolved                  # FS-resolved skills (project/user/host layers)
    harn skill inspect deploy            # one resolved skill's files + metadata
    harn skill match "deploy the app"    # rank skills against a prompt
    harn skill install owner/repo        # cache a git/local skill for the resolver
    harn skill new review-helper         # scaffold a new SKILL.md bundle
    
    # Provenance
    harn skill key generate --out signer.pem
    harn skill sign SKILL.md --key signer.pem
    harn skill endorse SKILL.md --key signer.pem
    harn skill verify SKILL.md
    harn skill who-signed SKILL.md
    harn skill trust add --from https://example.com/signer.pem
    

    harn demo#

    Run a bundled, fully-offline scenario that demonstrates Harn end-to-end without any API keys, project setup, or network access. Designed for the cold-start "what does Harn actually do?" moment.

    harn demo                         # interactive menu on a TTY; prints the list otherwise
    harn demo merge-captain           # persona-supervised PR triage with structured receipts
    harn demo review-captain          # HITL clarifying-question loop on a 5-file diff
    harn demo provider-race           # latency-aware provider race with cost attribution
    harn demo --list                  # one-line summary of every scenario
    harn demo merge-captain --json    # machine-readable summary of the run
    harn demo merge-captain --live    # re-run against the configured LLM provider
    harn demo merge-captain --no-record  # skip writing a `.harn-runs/demo-*` record
    

    Each scenario embeds a .harn script and a JSONL --llm-mock tape into the harn binary, so every demo is reproducible across machines and finishes in well under a second on the bundled tape. Successful runs write a run.json under .harn-runs/demo-<scenario>-<timestamp>/ that the portal can inspect.

    When --live is set the demo skips the bundled tape and routes through your configured provider. If the failure looks like a missing/invalid provider key the demo prints a hint to re-run without --live (or to run harn quickstart to wire a provider).

    harn quickstart#

    Inspect local provider readiness and write starter LLM configuration.

    harn quickstart
    harn quickstart --non-interactive
    harn quickstart --non-interactive --provider ollama --model llama3.2
    

    Quickstart reports existing provider credentials, Ollama reachability, free disk space, and GPU detection. It creates or updates:

    • HARN_PROVIDERS_CONFIG when set, otherwise ~/.config/harn/providers.toml
    • harn.toml in the current directory
    • .env in the current directory

    To pick up the selected provider/model defaults in your shell, run source .env before invoking Harn.

    harn doctor#

    One-command readiness check for new contributors and host integrators. The command surfaces actionable, secret-free output for both humans and machine consumers (IDE-host preflight, cloud-platform onboarding).

    harn doctor                # local checks; skips remote provider probes by default
    harn doctor --check-providers  # actively probe configured providers
    harn doctor --json         # versioned machine-readable output
    

    Each check reports a red/yellow/green status (fail / warn / ok, plus skip when a check is not applicable) along with:

    • A single-line detail.
    • An optional fix_command that the user can copy-paste.
    • An optional docs_url pointing at relevant documentation.
    • A blocks array naming workflows that fail when this check fails. Stable values are build, test, release, publish, portal, scripting, and editor.

    The command exits non-zero when at least one check is fail.

    Local diagnostic subprocesses have a five-second execution deadline and a 64 KiB output limit per stream. Explicit target compilation probes use a 30-second deadline. A timed-out tool is reported as a failed probe; an unreadable target inventory is a warning, not an empty successful inventory. Process cleanup may add a short grace period after the deadline.

    What it checks#

    • Toolchainrustc, cargo (FAIL when missing).
    • Optional dev toolscargo-nextest, sccache, actionlint (WARN when missing; each has a documented fallback).
    • Protocol artifacts — when run inside the harn repo, renders the protocol manifest this binary would write and compares it to the checked-in spec/protocol-artifacts/manifest.json, FAILing on drift.
    • Portal frontendnode, npm, and the portal's node_modules directory when run inside the repo.
    • Platform capabilitiesnotify file-watcher backend availability and the system browser opener used by OAuth and harn portal.
    • Provider configurationHARN_HOST_PROVIDERS_CONFIG, HARN_PROVIDERS_CONFIG, HARN_LLM_PROVIDER, the resolved secret-provider chain, and per-provider env vars (printed by name only — never by value).
    • Manifest — nearest harn.toml, declared MCP servers, and registered triggers.
    • Runtime state — event log backend, metadata cache, loaded skills, Ollama presence and pulled models, hardware snapshot.
    • Provider connectivity (--check-providers to include) — for OpenAI-compatible local providers, /v1/models probes parse the listing and report missing configured models instead of only checking HTTP reachability.

    JSON schema#

    The JSON document is versioned via the top-level schema_version string; patch releases never break the documented field shapes.

    {
      "schema_version": "1",
      "harn_version": "0.8.4",
      "providers_config_path": "...",
      "model_defaults": { ... },
      "checks": [
        {
          "id": "rustc",
          "label": "rustc",
          "status": "ok",
          "detail": "rustc 1.84.0 (...)",
          "fix_command": "https://rustup.rs",
          "docs_url": "https://www.rust-lang.org/tools/install",
          "blocks": ["build", "test", "release", "publish"]
        }
      ],
      "summary": {
        "ok": 18, "warn": 2, "fail": 0, "skip": 1,
        "blocked_flows": []
      },
      "hardware": { "ram_gb": 64, "gpu": "Apple Silicon (MPS available)", "free_disk_gb": 240 },
      "next_step": "..."
    }
    

    Secrets never appear in any output. Credential checks list the env var names they looked for, not the values.

    harn provider ready#

    Probe a configured provider's model inventory endpoint and optionally require a specific model alias or provider-native model id. Harn uses a catalogued model-list healthcheck such as /models or Ollama's /api/tags, or derives the inventory URL from an OpenAI-compatible /chat/completions endpoint.

    harn provider ready mlx --model mlx-qwen3.6
    harn provider ready mlx --base-url http://127.0.0.1:8002 --json
    

    The command exits non-zero for unsupported providers, unreachable servers, bad HTTP status, unparsable model listings, and missing models. It does not run local launcher scripts; host applications that auto-start local servers should report launch failures themselves and then call this probe again.

    harn provider capabilities audit#

    Check that every priced chat model in the loaded provider catalog has an explicit capability rule for both native_tools and preferred_tool_format. The command exits non-zero and lists suggested defaults when catalog rows need coverage.

    promote-from-eval ingests the parity overlay emitted by harn eval coding-agent and updates crates/harn-vm/src/llm/capabilities.toml with exact-model preferred_tool_format, tool_mode_parity, and tool_mode_parity_notes rows. The edit is idempotent.

    harn provider capabilities audit
    harn provider capabilities audit --json
    harn provider capabilities promote-from-eval .harn-runs/coding-agent-bench/latest/tool_mode_parity_overlay.toml
    

    harn provider effort-probe#

    Discover which reasoning-effort rungs a route actually accepts and diff that against the ladder the catalog declares in reasoning_effort_levels. Effort ladders are otherwise the one capability row nothing verifies — they are hand-written in capability fragments and hand-asserted in unit tests — so this command turns them into a measured fact. It sends one small live call per rung, so it costs money and needs credentials.

    Two drift directions are reported. declared_but_rejected means the catalog promises a rung the route refuses, so callers asking for it take a provider error. accepted_but_undeclared means the route serves a rung the catalog omits, so Harn's ladder snap quietly redirects callers away from a working rung.

    The probe suspends Harn's own declared-ladder check for its process, because that check refuses an out-of-ladder effort before the request leaves the process — with it on, the probe could only re-measure the catalog against itself and neither drift direction would be findable. --gated keeps the check on for a confirm-only run; rungs it blocks are reported as gated_locally rather than counted as provider verdicts.

    harn provider effort-probe --model glm-5.3
    harn provider effort-probe --model glm-5.3 --suggest-fragment
    harn provider effort-probe --all-declared --one-per-claim --plan
    harn provider effort-probe --all-declared --one-per-claim --fail-on-drift --json
    

    --plan lists the routes and the resulting call count without calling anything. --one-per-claim probes one representative per distinct (provider, ladder) claim, since a catalog ladder is a rule matching many models; each result names the routes it stands for. --suggest-fragment prints a corrected capability row for review — the probe never edits catalog TOML.

    harn provider option-probe#

    Send one portable generation option through one concrete provider/model endpoint and compare the provider verdict with the capability catalog. The accepted options are temperature, top_p, top_k, seed, frequency_penalty, presence_penalty, and stop.

    harn provider option-probe anthropic \
      --model claude-opus-4-7 \
      --option temperature \
      --plan --json
    
    harn provider option-probe anthropic \
      --model claude-opus-4-7 \
      --option temperature \
      --fail-on-drift --json
    

    --plan reports the endpoint, exact catalog field, claim, and one-request call count without contacting the provider. A live report records match, drift, or unmeasured. Provider acceptance and provider rejection are measurements; authentication failures, throttling, unavailable models, and local gates are not. With --fail-on-drift, drift exits 1 and an unmeasured request exits 2.

    The command normally suspends catalog shaping for only the selected option, so a negative claim can be falsified at the wire. The typed authority is captured in the resolved call and carried through spawned transport work; it does not affect sibling calls or other options. --gated leaves normal shaping enabled for a confirm-only run. Acceptance proves the endpoint accepted a meaningful, non-default value, not that a provider necessarily honored the value.

    harn provider dispatch-explain#

    Explain the resolved provider dispatch configuration for one provider/model route without making a network call. This is useful when checking why a route uses a specific wire format, tool-call format, base URL host, or thinking-mode setting.

    harn provider dispatch-explain anthropic claude-sonnet-5 --json
    harn provider dispatch-explain openrouter anthropic/claude-sonnet-4.5 --thinking --tool-format native
    

    harn provider dispatch-audit#

    Audit deterministic dispatch resolution across catalog routes and tool-format/thinking variants without making provider calls. The default matrix checks default, thinking, native, text, and json variants for every catalog route and emits a JSON report suitable for CI or provider catalog review. JSON output uses schema_version: 3 and includes the audited catalog's Blake3 hash plus provider, model, and route counts so downstream live-probe results can be tied to the exact catalog universe they measured. It also reports unrouted_providers: catalog providers with zero route rows, including a typed reason, so credentialed or configured providers that cannot enter route-level live probes are visible as catalog-quality work rather than silently absent from the empirical matrix.

    Pass --include-tool-probe-plan to include a post-freeze live-probe execution manifest for the selected routes. The manifest uses stable IDs tied to the catalog hash and structured argv arrays rather than shell strings, so automation can run the probes without re-parsing command text. The plan starts with readiness_commands: one harn provider ready --model ... --json preflight per selected route, with its own stable id, argv, and output_path. Tool-probe commands then carry an explicit request_profile, a runnable argv, and an output_path under the same catalog-hash-derived output_dir so live receipts can be resumed and tied back to the exact audit plan. Readiness and tool-probe commands both carry catalogued secret_envs key names when the route accepts credentials through one or more environment variables; secret values are never included. The filenames include the command id prefix as well as readable route/case slugs so lossy path sanitization cannot collide distinct probe commands; override the directory with --tool-probe-output-dir when staging a named run. The manifest also includes a matrix summary; provider_model_count and route_count are the empirical provider/model breadth, while bare model_count is only the unique model-name count, and readiness_command_count is the expected preflight receipt count. The manifest also lists live_request_profiles and excluded_request_profiles: live tool-probe commands intentionally use catalog_default, while parameter_edges remains covered by offline provider tool-probe-audit request validation because non-default request profiles are dry-run only. The default plan also records excluded_cases; it covers the broad live tool-probe cases except signed-thinking by default. Add --tool-probe-case signed_thinking_tool_result_followup when auditing that provider-specific dialect explicitly. Explicit signed-thinking cases are still filtered per route; unsupported routes are reported under not_applicable_commands with the skipped route, case, request profile, and mode instead of emitted as invalid live commands. A route can therefore have a readiness command and zero tool-probe commands when the selected live case is not applicable to that route.

    --route provider:model is fail-closed: malformed route filters and well-formed filters that match no catalog route both produce a non-zero audit report, so a typo cannot silently yield an empty live-probe plan. --provider filters are fail-closed too: unknown provider ids and providers with zero routing routes produce typed failures instead of a generic empty report. --model and --capability are repeatable route filters for focused provider/model/capability sweeps. They also fail closed when the requested model or capability matches no selected catalog route.

    harn provider dispatch-audit
    harn provider dispatch-audit --provider anthropic --provider gemini
    harn provider dispatch-audit --provider anthropic --model claude-sonnet-5 --capability tools
    harn provider dispatch-audit --route anthropic:claude-sonnet-5 --variant native
    harn provider dispatch-audit --provider anthropic --include-tool-probe-plan --tool-probe-repeat 3
    harn provider dispatch-audit --provider openrouter --include-tool-probe-plan --tool-probe-output-dir .harn-runs/provider-live-probes/openrouter-smoke
    

    harn provider probe#

    Snapshot a provider's readiness and local loaded-model state as JSON. For Ollama, the output also includes /api/ps memory/context details. When a model is supplied, local runtime profile metadata is included for eval pipelines.

    harn provider probe ollama --model devstral-small-2
    harn provider probe mlx --model mlx-qwen3.6 --base-url http://127.0.0.1:8002
    

    harn provider tool-probe#

    Run a harmless one-tool conformance probe. The command asks the model to call echo_marker({value}), tests streaming and non-streaming modes by default, and emits JSON with native/text/disabled fallback classification.

    harn provider tool-probe ollama --model devstral-small-2
    harn provider tool-probe llamacpp --model local-qwen3.6 --mode non-streaming
    harn provider tool-probe dashscope --model qwen3.6-35b-a3b --mode non-streaming --repeat 5
    harn provider tool-probe openai --model gpt-5.4-mini --tool-format json
    

    Use --response-fixture to classify a saved provider response without making a network request. Use --repeat for live reliability checks; repeated summaries only pass when every attempted probe for that mode succeeds. harn local switch can consume the JSON with --probe-result. --tool-format native|json|text forces the live emission contract and exact parser; it does not enable the permissive adaptive parser. Probes preserve route generation defaults and raise only the minimum output budget when necessary to leave visible tool-call headroom after the route's resolved reasoning allowance.

    harn provider tool-calibrate#

    Run the fixed live micro-case battery across a route × format matrix and write a versioned tool-format fitness snapshot. Records are keyed by provider, model, format, and case and include pass rate, classification histogram, latency, and token totals. Route recommendations select the highest pass rate, then lower latency and token use.

    harn provider tool-calibrate \
      --route openai:gpt-5.4-mini \
      --route ollama:qwen3:8b \
      --repeat 5 \
      --output .harn/tool-format-fitness.json
    

    Set HARN_TOOL_FORMAT_FITNESS_PATH before starting Harn to use a reviewed snapshot. Harn reads it once per process, so changing the file during an eval cannot change that eval's tool format. Explicit alias pins still win; without a measured recommendation Harn falls back to the capability catalog and then the global default. Snapshot recommendations can only select native, json, or text, never adaptive.

    harn provider tool-scorecard#

    Aggregate one or more harn provider tool-probe --json reports into a stable route scorecard. Fixture input is offline-only: the command reads saved probe reports and does not call providers. The JSON report uses schema_version: 8 and includes route-level catalog_claim, catalog_mismatches, and suggested_catalog_updates fields plus a fitness store with exact provider/model/format/case observations. Suggested catalog updates remain advisory and are only emitted for positive observed tool-call evidence.

    harn provider tool-scorecard --tool-probe-report ./probe.json
    harn provider tool-scorecard --tool-probe-report ./probe-a.json --tool-probe-report ./probe-b.json --json=false
    harn provider tool-scorecard --tool-probe-report ./probe.json --markdown > scorecard.md
    

    Use --plan-from-catalog to render the fixed micro-case matrix for catalogued routes before probing. Plan output remains schema_version: 1.

    harn provider tool-scorecard --plan-from-catalog --route anthropic:claude-sonnet-5
    harn provider tool-scorecard --plan-from-catalog --route anthropic:claude-sonnet-5 --markdown > scorecard-plan.md
    

    harn local launch#

    Bring a local model up through Harn's provider catalog:

    harn local launch devstral-small-2:24b --provider ollama --json
    harn local launch local-qwen3.6 --provider llamacpp --model-source ~/models/qwen3.6/Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf --ctx 8192
    harn local launch mlx-qwen3.6 --provider mlx --model-source unsloth/Qwen3.6-35B-A3B-UD-MLX-4bit
    harn local launch local-gemma4-e4b --provider vllm \
      --model-source google/gemma-4-e4b-it --lora-adapter tools=org/tools-lora
    

    Ollama launch warms the daemon and persists the active selection. llama.cpp MLX, and vLLM launch the cataloged server command, record a PID/log under <state_root>/local/, wait for /v1/models, and then let harn local stop clean up the process. Provider mechanics are data-driven from [providers.<id>.local_runtime] in the generated provider catalog, so local overlays can adjust command names, ports, arg names, or model-source env vars. When a model row includes [models.<id>.local_memory], launch preflights the requested context against current available RAM and returns a memory_plan in JSON mode. Use a smaller --ctx or free RAM when the guard trips; pass --allow-memory-risk only to override an intentionally conservative estimate. For runtimes whose catalog row declares LoRA launch flags, repeat --lora-adapter NAME=PATH_OR_REPO to expose startup adapters; when exactly one adapter is loaded, Harn records that adapter name as the selected request model. Provider catalog rows also own the module value shape: the default is the portable NAME=PATH_OR_REPO form, while rows such as vLLM can opt into lineage-preserving JSON values with the served base model recorded as base_model_name.

    harn local profile#

    Explain the local runtime risk profile for a model/provider route:

    harn local profile devstral-small-2 --provider ollama
    harn local profile ollama-gemma4 --json
    

    Statuses are preferred, experimental, vision_only_experimental, quarantined, or unknown. harn local switch refuses experimental and quarantined profiles unless the required probes pass or --force is supplied.

    harn models list#

    Query the same provider/model catalog Harn resolves at runtime. The human view uses a deterministic table; --json returns the complete catalog rows rather than a lossy display projection, including pricing.cache_read_per_mtok, tool_mode_parity, and tool_mode_parity_notes where known.

    harn models list --where tier=frontier,strengths=coding --sort pricing.input \
      --columns id,provider,pricing.input,pricing.cache_read,context_window,tool_support.parity,tool_support.parity_notes
    harn models list --provider anthropic --where open_weight=false --sort context_window
    harn models list --where tool_support.parity=native_unreliable --json
    

    Repeat --where KEY=VALUE for additional all-of filters, or separate entries with commas. Supported fields are provider, tier, strengths (exact tag membership), tool_support.parity, and open_weight. Sort fields are pricing.input, pricing.output, pricing.cache_read, and context_window; numeric sorts are ascending with missing metadata last. --columns selects a strict allowlist for the human table and cannot be combined with --json, which intentionally returns complete authoritative rows.

    harn models test#

    Round-trip a small prompt through one resolved model and report model id, provider, latency, first streamed delta timing, token usage, and estimated cost. When Harn has no price for the resolved route, JSON returns "estimated_cost_usd": null and text output says estimated_cost_usd=unavailable. A returned 0 is an exact zero cost.

    harn models test gpt-5.4-mini --prompt "Reply with pong."
    harn models test qwen3:30b --provider ollama --json
    

    --provider bypasses provider inference for the model selector. The command uses the configured provider client path, so it also respects provider credentials, base URL overrides, and HARN_LLM_CALLS_DISABLED.

    harn models info#

    Print resolved model metadata as JSON. For Ollama models, --verify probes /api/tags and checks the selected tag. --warm implies --verify and sends an empty /api/generate request to preload the matched tag.

    harn models info llama3.2:latest
    harn models info --verify llama3.2
    harn models info --warm --keep-alive 30m llama3.2
    

    Ollama readiness failures use stable readiness.status values, including daemon_down, bad_status, invalid_response, model_missing, and warmup_failed. --verify and --warm exit non-zero when readiness fails.

    harn models batch plan#

    Plan provider Batch API use for latency-tolerant model workloads:

    harn models batch plan --workload eval
    harn models batch plan --provider openai --min-discount-percent 50 --json
    harn models batch plan --model gpt-5.4-mini --max-turnaround-hours 24
    

    The planner reads Harn's provider catalog and lists routes whose provider capabilities advertise async batch support. Harn owns the reusable batch manifest, prepare, submit, and receipt workflow so Burin or other hosts can route lower-priority eval, judge, corpus refresh, and distillation workloads to discounted batch lanes without leaking provider-specific policy into product code. Subscription-plan CLI auth is reported as separate from API batch credentials; providers that expose Batch APIs still require provider API billing.

    Human output marks each route as live submit or dry-run only. JSON output includes batch.harn_live_adapter.{submit,status,cancel,download} plus Harn's typed batch lifecycle facts: wire/input mode, published discounts and turnaround, request/file ceilings, result ordering, per-request failure semantics, cancellation support, retention, non-secret storage notes, and non-secret operational notes such as retry and result-rejoin constraints. Provider Batch API capability stays separate from Harn's current live adapter coverage. Routes with batch_api: true but no Harn live adapter can still be planned, manifested, and prepared for offline adapter work; submit, status, cancel, and download fail before network calls until that provider adapter exists. Treat async batch receipts as offline eval/corpus artifacts, not as live interactive agent-loop pass@1 evidence for the meter stick.

    harn models batch manifest#

    Build a durable, provider-neutral manifest for a JSONL request ledger:

    harn models batch manifest --provider openai --model gpt-5.4-mini \
      --requests ./requests.jsonl --out ./batch-manifest.json
    harn models batch manifest --provider openai --model gpt-5.4-mini \
      --requests ./requests.jsonl --out ./batch-manifest.json --tool-format json --json
    

    Each input row must be a JSON object. Rows can declare provider, model, tool_format, endpoint, custom_id, and metadata; omitted provider/model fields fall back to the CLI flags. Harn resolves every row through the same catalog metadata as harn models batch plan, groups requests by provider, model, provider batch wire format, endpoint, and tool-call convention, then writes a manifest with stable request ids and per-row hashes.

    manifest still does not submit work to providers. It is the durable planning artifact that lower-priority eval, judge, corpus-refresh, and distillation jobs can hand to provider-specific upload/poll/download adapters without leaking provider batch details into Burin or other host products.

    harn models batch execute#

    Own a complete provider-batch lifecycle in one durable execution directory:

    harn models batch execute init --requests ./requests.jsonl \
      --execution-dir ./.harn/batches/eval-001 --provider openai --model gpt-5.4-mini
    harn models batch execute advance --execution-dir ./.harn/batches/eval-001 --json
    harn models batch execute inspect --execution-dir ./.harn/batches/eval-001 --json
    harn models batch execute cancel --execution-dir ./.harn/batches/eval-001 --json
    

    init copies the request ledger into the execution directory, writes the manifest and prepared request files, and creates the authoritative harn.model_batch_execution_receipt. Each advance performs at most one legal transition: submit, status, download, or normalized rejoin. Call it again after a non-terminal status poll. inspect verifies the ledger, manifest, prepared files, intermediate receipts, raw results, stable request/job identities, and every recorded digest before reporting state.

    Execution updates are lock-serialized and compare-and-swap protected. Before a provider create, Harn persists a write-ahead operation id and the adapter's declared recovery mode. Bedrock-style deterministic create tokens may reuse that id; adapters without an equivalent documented token are reconcile-only. If a create may have been accepted but no receipt exists, the execution reports reconciliation required and advance will not create another paid batch.

    --dry-run follows the same state machine with zero provider calls. Its Harn-owned fixture transport produces provider-shaped raw results, so tests and consumers do not edit status receipts or synthesize completion outside Harn. When the execution reaches rejoined, consumable is true only if every manifest id has exactly one successful terminal row.

    harn models batch prepare#

    Prepare provider-native request artifacts from a model batch manifest:

    harn models batch prepare --manifest ./batch-manifest.json --out-dir ./.harn/batches/eval-001
    harn models batch prepare --manifest ./batch-manifest.json --out-dir ./.harn/batches/eval-001 --json
    

    prepare reads the provider-neutral manifest and writes one request artifact per batch group plus a deterministic receipt.json. The request artifacts use the provider's batch envelope (openai/mistral JSONL, Fireworks {custom_id, body} JSONL, Gemini {key, request} JSONL, or Anthropic Message Batches JSON) while the receipt records the stable manifest hash, request-file hashes, provider operation, upload/create shape, and result-rejoin ids. It does not read credentials or call provider APIs; harn models batch submit consumes this receipt as its durable input. Prepare receipts and job entries also include a provider-neutral lifecycle object with phase, normalized state, terminal/result/cancel/retry booleans, and per-state counts so consumers do not need to scrape status strings.

    harn models batch submit#

    Validate and optionally submit prepared provider-native batch jobs:

    harn models batch submit --receipt ./.harn/batches/eval-001/receipt.json \
      --out ./.harn/batches/eval-001/submission.json --dry-run
    harn models batch submit --receipt ./.harn/batches/eval-001/receipt.json \
      --out ./.harn/batches/eval-001/submission.json --json
    

    --dry-run verifies the prepare receipt, re-hashes every request file, renders the provider operation with credential names redacted, and writes a durable harn.model_batch_submission_receipt without network calls. Live submit currently supports OpenAI-compatible batch jobs for OpenAI, Azure OpenAI, Groq, Together, and Parasail; Mistral file-backed batch jobs; Fireworks dataset-backed batch jobs; Gemini File API JSONL batches; Anthropic Message Batches; xAI batches; and catalog-eligible Amazon Bedrock model/region jobs backed by typed S3 input/output locations. Provider API keys must be present in the provider's normal environment variable (OPENAI_API_KEY, GROQ_API_KEY, TOGETHER_AI_API_KEY or TOGETHER_API_KEY, PARASAIL_API_KEY, MISTRAL_API_KEY, FIREWORKS_API_KEY, GEMINI_API_KEY or GOOGLE_API_KEY, ANTHROPIC_API_KEY, XAI_API_KEY, AZURE_OPENAI_API_KEY, AZURE_OPENAI_AD_TOKEN, or AZURE_OPENAI_BEARER_TOKEN); Bedrock uses the AWS SDK credential and region chains rather than a provider API key. Fireworks live submit/status/download also needs HARN_BATCH_FIREWORKS_ACCOUNT_ID or FIREWORKS_ACCOUNT_ID. Subscription-plan auth remains out of scope for provider Batch APIs. The submission receipt records provider job ids, status, request file hashes, and result handles for later poll/download/rejoin work. Submission receipts carry the same normalized lifecycle object as prepare receipts; dry-run jobs remain ready while the receipt lifecycle state is dry_run.

    harn models batch status#

    Poll provider state for submitted model batch jobs:

    harn models batch status --submission ./.harn/batches/eval-001/submission.json \
      --out ./.harn/batches/eval-001/status.json --dry-run
    harn models batch status --submission ./.harn/batches/eval-001/submission.json \
      --out ./.harn/batches/eval-001/status.json --json
    

    --dry-run validates the submission receipt and summarizes cached job state without network calls. Live status currently polls OpenAI/Groq/Together- compatible batches/{id}, Azure OpenAI batches/{id}, Bedrock model-invocation-job/{id}, Fireworks batchInferenceJobs/{id}, Gemini batches/{id}, Anthropic Message Batches, Mistral batch jobs, and xAI batches from the Harn provider adapter boundary, then writes a harn.model_batch_status_receipt with stable job ids, normalized lifecycle state, provider status, and result file pointers. The embedded lifecycle object is the stable state-machine contract for eval/corpus runners; top-level counts remain a human-friendly summary.

    harn models batch cancel#

    Cancel submitted model batch jobs and write a durable cancellation receipt:

    harn models batch cancel --receipt ./.harn/batches/eval-001/submission.json \
      --out ./.harn/batches/eval-001/cancel.json --dry-run
    harn models batch cancel --receipt ./.harn/batches/eval-001/status.json \
      --out ./.harn/batches/eval-001/cancel.json --json
    

    cancel accepts either a submission receipt or a latest status receipt. In --dry-run mode it validates provider job ids and records redacted cancellation operations without network calls. Live cancellation is implemented only where the provider catalog says cancellation is supported and Harn has a known adapter: OpenAI and Azure OpenAI batch jobs, Amazon Bedrock model invocation jobs, Anthropic Message Batches, and Gemini Batch API operations. Jobs that are already terminal, missing provider ids, or backed by providers with unknown cancellation semantics are skipped with structured skip_reason values rather than treated as hard failures. The command writes a harn.model_batch_cancel_receipt with per-job cancel operations, provider responses, skipped-job reasons, normalized canceling/canceled lifecycle states, and aggregate lifecycle counts.

    harn models batch download#

    Download result files for completed model batch jobs:

    harn models batch download --status ./.harn/batches/eval-001/status.json \
      --out-dir ./.harn/batches/eval-001/results --dry-run
    harn models batch download --status ./.harn/batches/eval-001/status.json \
      --out-dir ./.harn/batches/eval-001/results --json
    

    --dry-run validates the status receipt, requires completed jobs, and records the redacted provider download operations without network calls. Live download currently retrieves OpenAI/Azure OpenAI/Groq/Together-compatible, Gemini, and Mistral file content plus Bedrock S3 output, Fireworks output dataset signed URLs, Anthropic Message Batch results_url, and xAI result pages, writes provider JSONL files under --out-dir, and emits a harn.model_batch_results_receipt containing artifact paths, handles, hashes, source receipt metadata, and normalized download lifecycle counts. Use --max-bytes to cap each provider file response when working with very large batches.

    harn models batch rejoin#

    Normalize downloaded provider rows and rejoin them by manifest id:

    harn models batch rejoin \
      --execution ./.harn/batches/eval-001/execution.json \
      --manifest ./.harn/batches/eval-001/manifest.json \
      --download ./.harn/batches/eval-001/results/receipt.json \
      --out-dir ./.harn/batches/eval-001/rejoin --json
    

    rejoin verifies the durable execution plus the complete manifest → prepare → submission → status → download chain before consuming rows. It also re-hashes prepared request files and downloaded artifacts. Provider-native identifiers (custom_id, customId, key, request_id, recordId, and nested ids) and terminal response/error variants normalize into manifest-ordered rows with stable execution, group, job, request, and source artifact identities.

    The raw provider files remain byte-preserved and hash-bound. Duplicate, missing, unexpected, id-less, malformed, provider-error, partial-job, wrong-manifest, or changed-artifact inputs emit a non-consumable harn.model_batch_rejoin_receipt with exact counts, ids, and quarantine reasons. Downstream consumers should read only normalized.jsonl after the receipt says consumable: true; they should never branch on provider-native fields.

    harn models lora export#

    Export a tool-calling corpus into a trainer-ready LoRA dataset:

    harn models lora export --base local-gemma4-e4b --provider vllm --tool-format auto \
      --corpus ./lora-corpus --out ./train.jsonl --manifest ./train.manifest.json
    harn models lora export --base local-gemma4-e4b --provider vllm --tool-format native \
      --corpus ./lora-corpus --modules-to-save embed_tokens,lm_head --check --json
    

    The export resolves the same provider capability matrix as model calls and emits either native messages/tools rows or Harn text-tool rows. Reports, row metadata, and manifests include a stable LoRA contract id derived from the base model, provider, effective Harn tool format, dataset format, chat template, and tool-catalog policy. Use that id to keep training, adapter inspection, eval, and serving on the same wire contract even when adapter paths or request-model names change. The manifest's contract.training_contract block also records the assistant mask policy, packing policy, parser owner, split policy, and PEFT modules_to_save / embedding-head save policy so trainers can verify the SFT setup without scraping human-readable notes. The default policy is adapter-only; declare --modules-to-save embed_tokens,lm_head only when tokenizer resize or output-head training requires it, and keep the weight-tying evidence in target metadata before merging adapters. --tool-catalog-policy full_schema is the default production contract: prompts carry the full tool schemas used to validate the dataset. compressed_names and fixed_catalog_internalized are explicit fixed-catalog experiment modes; they require --tool-catalog-id or --tool-catalog-hash and are hashed into the LoRA contract so names-only or no-catalog adapters cannot reuse full-schema manifests. --check validates conversion without writing JSONL rows.

    harn models lora manifest#

    Write a canonical LoRA training-run manifest without invoking a trainer:

    harn models lora manifest --base local-gemma4-e4b --provider vllm \
      --tool-format json --dataset ./train.jsonl --export-manifest ./train.manifest.json \
      --adapter-name burin-tools --adapter-path ./adapter --out ./adapter.manifest.json \
      --modules-to-save embed_tokens,lm_head
    harn models lora manifest --base local-gemma4-e4b --provider vllm \
      --trainer unsloth_sft --method qlora --rank 24 --training-run-id run-123 --json
    

    The command records the resolved base route, effective tool-call format, Harn LoRA contract id, trainer hyperparameters, corpus/export inputs, adapter artifact paths and hashes when local files exist, serving hints, serving.serving_requirements, and the promotion/eval recipe. It is intentionally trainer-agnostic: Python, MLX, Unsloth, TRL, cloud, or future Harn-native trainers can all call this surface after a run and produce the same manifest shape. The resulting JSON is accepted by harn models lora inspect --manifest, so adapter promotion can compare the training contract with the served route before any eval or launch.

    harn models lora train#

    Render or execute a named LoRA trainer backend and write a training receipt:

    harn models lora train --base local-gemma4-e4b --provider vllm \
      --tool-format json --dataset ./train.jsonl --export-manifest ./train.manifest.json \
      --output-dir ./adapter --receipt-out ./adapter/train.receipt.json \
      --adapter-name burin-tools --request-model burin-tools --trainer unsloth_sft \
      --modules-to-save embed_tokens,lm_head --json
    harn models lora train --base local-gemma4-e4b --provider vllm \
      --dataset ./train.jsonl --output-dir ./adapter --execute -- \
      uv run python train.py config/e4b.yaml
    

    Without --execute, the command is a deterministic dry-run: it hashes local input files when present, records trainer/backend name and version, resolves the same Harn LoRA contract as plan/export/manifest, and emits the post-training harn models lora manifest plus harn models lora inspect commands. The receipt also includes a structural dataset audit with parse-error, invalid-tool-block, schema-repair, no-tool, unavailable-tool, parallel-call, multi-turn, and tool-result counters. With --execute, Harn launches only the caller-supplied backend argv and records the exit status in the same receipt shape. Python, Unsloth, TRL, MLX, Modal, or cloud trainers remain backend shims; Harn owns config normalization, route metadata, serving requirements, manifest shaping, and promotion receipts. Dry-runs with no backend argv set backend.argv_required instead of inventing a trainer command. --modules-to-save is part of the hashed LoRA contract and is forwarded to the post-training manifest command, so PEFT embedding/head saves cannot silently drift between export, train, manifest, inspect, and promotion. --tool-catalog-policy, --tool-catalog-id, and --tool-catalog-hash are also forwarded into backend recipes and post-training manifests when fixed-catalog compression experiments intentionally train for names-only or no-catalog inference.

    harn models lora preflight#

    Check a tool-calling corpus before spending GPU time on LoRA training:

    harn models lora preflight --base local-gemma4-e4b --provider vllm \
      --corpus ./lora-corpus --config ./config/e4b.yaml --done-marker '##DONE##' --check
    harn models lora preflight --base google/gemma-4-e4b-it \
      --corpus ./lora-corpus/burin-tool-calling-corpus.jsonl \
      --source-tool-format json --min-records 190 --json
    

    The preflight is CPU-only. It resolves the target model route, reads max_seq_length, min_fit_ratio, and min_records from a lightweight config file when provided, then checks record count, approximate sequence-budget fit, hard token outliers, source tool-call body format, malformed tool-call blocks, undeclared tool names, typed behavior-strata coverage, and an optional caller-supplied done marker. Behavior-strata policy defaults to --behavior-strata-policy strict. The explicit legacy-unclassified policy lets an old corpus with no declared behavior metadata pass preflight for migration review, but its typed report remains status: "legacy_unclassified", retains all five missing classes, lists the stable record ids that need review, and prints a warning. A command-line threshold overrides the matching config value. --check requires a record floor from --min-records or min_records in the config, so an unset gate cannot report success. If even one trainable record declares a behavior class, missing coverage is incomplete and remains an error under both policies. Export has no migration policy and always requires real emitted coverage. Use --check to make readiness failures exit non-zero. The command intentionally owns reusable LoRA corpus readiness in Harn; product-specific markers or thresholds should be passed as flags rather than hard-coded into hosts.

    harn models lora inspect#

    Inspect a PEFT LoRA adapter directory or repo id against a Harn model route:

    harn models lora inspect --base local-gemma4-e4b --provider vllm ./tools-lora
    harn models lora inspect --base local-gemma4-e4b --provider vllm --name tools org/tools-lora --json
    

    The report checks local adapter_config.json metadata when present, compares base_model_name_or_path with the resolved Harn model id, shows the route's tool-calling capability metadata, and prints the matching harn local launch command. Inspect JSON also includes serving.serving_requirements, so promotion automation can verify parser ownership, runtime flags, and template metadata for the served adapter route. When local metadata declares a LoRA rank and the cataloged runtime has a max-rank flag, the launch command includes --max-lora-rank so runtime defaults cannot silently under-provision the adapter. Remote adapter ids are treated as runtime-resolved and warned rather than rejected.

    harn models lora plan#

    Plan a portable LoRA or QLoRA fine-tune for a Harn model route:

    harn models lora plan --base local-gemma4-e4b --provider vllm --tool-format auto --corpus ./lora-corpus
    harn models lora plan --base local-gemma4-e4b --provider vllm --trainer unsloth_sft \
      --teacher dashscope/qwen3-coder-next --corpus ./lora-corpus \
      --modules-to-save embed_tokens,lm_head
    harn models lora plan --base google/gemma-4-E4B-it --provider google --tool-format native --json
    

    The plan resolves the same provider capability matrix used by agent calls, records the effective tool-call format, and prints a training/eval/launch recipe. The report includes an explicit template block so native Gemma 4 or FunctionGemma training stays on the model tokenizer's function-calling template, while Harn text/json adapters target the Harn <tool_call> parser convention. Use this before exporting a corpus so train, eval, and runtime serving all agree on the same tool-call contract. Add --teacher <model> to plan a synthetic distillation or corpus-refresh lane without starting any model calls. With the default --corpus-strategy auto, Harn chooses refresh when a corpus is present, distill when only a teacher is present, and audit-only otherwise. The report lists provenance manifest fields, hard-negative slices, and holdout gates so generated data cannot silently contaminate evaluation fixtures. It also lists the trainer contract Harn expects for tool-calling SFT, including assistant-only loss masks, messages/tools columns, packing boundaries, and method-specific target modules. Omitting --trainer uses the neutral external_sft_trainer contract. Use --trainer trl_sft_trainer, --trainer unsloth_sft, or --trainer external_sft_trainer to pin a backend-specific contract explicitly; Harn still owns export, manifests, eval, and serving promotion, while trainer-specific runtimes only fit the adapter weights. QLoRA plans use PEFT's all-linear target module shorthand; full LoRA plans keep explicit attention projection modules in training.target_modules. Plans default to no PEFT modules_to_save, which keeps tool-calling adapters small and avoids accidental embedding/LM-head divergence. When embedding or head modules must be saved, training.contract.peft_save_policy records the declared modules, embedding save policy, and whether a weight-tying check is required before adapter merge. The corpus_refresh.model_aware_selection block records base-model failure buckets, parser/schema difficulty signals, sampling policy, refinement loops, and stop conditions so corpus refresh jobs prioritize failures the target model almost solves instead of adding near-duplicate syntax examples. The JSON report exposes the same machine-readable contract under training.contract for automation that prepares TRL/PEFT trainer configs. The serving.serving_requirements array records machine-readable serving requirements such as parser ownership, vLLM tool-parser flags, chat-template ids, required manifest metadata, and promotion gates. Plan, manifest, and inspect automation should satisfy those requirements before serving or promoting an adapter instead of scraping runtime notes. The evaluation.evidence_contract block adds the promotion receipt contract: stable promotion id, paired base/adapter routes, required preflight, export, manifest, inspect, tool-probe, and eval receipts, optional Batch API receipts, and a harn models batch manifest command for latency-tolerant tool-call eval rows. The launch block includes the matching harn models lora export command so the trainer dataset, provenance manifest, eval route, and serving route share the same base model, provider, tool format, and chat-template contract. Use --tool-catalog-policy compressed_names or --tool-catalog-policy fixed_catalog_internalized only for fixed-catalog experiments, and pair it with --tool-catalog-id or --tool-catalog-hash. The policy controls whether inference still includes full tool schemas, compressed tool names only, or no runtime catalog; it is recorded in training.contract.tool_catalog, included in the LoRA contract id, and forwarded to export, train, and manifest commands. Use --rank, --alpha, and --dropout to pin adapter hyperparameters in the same contract; when the runtime exposes a LoRA rank flag, the local launch hint uses the planned rank as --max-lora-rank so serving buffers match training. The serving.lora_module_value_format field records whether the runtime uses the portable NAME=PATH_OR_REPO adapter value or a cataloged richer value such as vLLM's base-model-lineage JSON. For native Gemma 4 routes on vLLM, the plan also warns that the gemma4 tool-call parser and chat template are part of the serving contract. Validate or promote adapters with serialized native-tool traffic unless the pinned vLLM parser version is already proven concurrency-safe for the route.

    harn models recommend#

    Recommend a starter model from the local hardware snapshot and configured cloud credentials. The command prefers an Ollama model when no cloud provider key is set, and emits the selected model plus the one-line rationale.

    harn models recommend
    harn models recommend --json
    

    The JSON output includes the hardware snapshot used for the lookup: available RAM, GPU/MPS acceleration, and free disk space.

    harn connect#

    Authorize connector providers and store local connector secrets in the workspace keyring namespace.

    harn connect github \
      --app-slug my-harn-app \
      --app-id 12345 \
      --private-key-file app.pem
    harn connect slack \
      --client-id "$SLACK_CLIENT_ID" \
      --client-secret "$SLACK_CLIENT_SECRET" \
      --scope "app_mentions:read chat:write"
    harn connect linear \
      --client-id "$LINEAR_CLIENT_ID" \
      --client-secret "$LINEAR_CLIENT_SECRET"
    harn connect notion \
      --client-id "$NOTION_CLIENT_ID" \
      --client-secret "$NOTION_CLIENT_SECRET"
    harn connect generic acme https://mcp.example.com/mcp
    harn connect --generic acme https://mcp.example.com/mcp
    harn connect acme
    harn connect duffel --from-env DUFFEL_TEST_KEY
    harn connect --list
    harn connect --refresh notion
    harn connect --revoke slack
    

    OAuth provider commands use a plaintext HTTP loopback callback bound to 127.0.0.1 or localhost. The default redirect URI uses port 0, so Harn selects a random free localhost port and sends that exact URI in the authorization request; custom redirect URIs must also include an explicit port. PKCE S256 is always enabled. Generic OAuth discovers protected-resource and authorization-server metadata when explicit endpoints are not supplied, attempts dynamic client registration when available, and sends the resource parameter to both the authorization and token endpoints. harn mcp login derives that resource from the canonical MCP server URI: lowercase scheme and host, no default port, no query string, no fragment, and no trailing slash.

    harn connect <provider> reads authentication metadata for providers in the nearest harn.toml [[providers]] table. OAuth metadata starts the browser flow; flags such as --client-id, --scope, --auth-url, and --token-url override that metadata for one run.

    For auth_type = "api-key" with one outbound required_secrets entry, the same command prompts without echoing the key. Inbound verification secrets do not make the target ambiguous. --from-env NAME and --value-file PATH read the value from that source and store it in the configured keyring; the option contains only the source name or path, never the secret. A connector with several outbound credentials returns the exact harn connect api-key command for each target so their values cannot be mixed up.

    Connectors can declare credential_environment mappings from logical secret ids to accepted environment-variable names. harn connect status then treats a declared, non-blank variable as ready even when the local keyring is not available. JSON and table output show the variable name but never its value. Undeclared variables are ignored. If a keyring write fails, the error names the declared variables that can satisfy that exact secret on a headless host. See Headless API-key setup for the operator steps.

    Stored OAuth tokens are written under connector-friendly secret ids:

    • <provider>/access-token
    • <provider>/refresh-token when the provider returns one
    • <provider>/oauth-token for the full local refresh metadata

    harn connect --list reads a small keyring index and shows token expiration and last-used metadata when known. --refresh <provider> forces a refresh-token grant. --revoke <provider> removes the local OAuth token, access token, refresh token, and index entry.

    Provider-specific OAuth flags:

    FlagDescription
    --client-id <id>Pre-registered OAuth client id
    --client-secret <secret>OAuth client secret
    --scope <scopes>Requested scope string
    --resource <resource>Override the OAuth resource indicator
    --auth-url <url>Override the authorization endpoint
    --token-url <url>Override the token endpoint
    --token-auth-method <method>none, client_secret_post, or client_secret_basic
    --redirect-uri <uri>Override the loopback callback URI
    --no-openPrint the authorization URL instead of opening a browser

    The GitHub command captures GitHub App installation metadata. If --app-id and --private-key-file are supplied, it stores the private key as github/app-private-key. If --webhook-secret or --webhook-secret-file is supplied, it stores github/webhook-secret.

    harn connect linear#

    Register a Linear webhook through the GraphQL webhookCreate mutation using the Linear triggers declared in the nearest harn.toml.

    The command derives resourceTypes from [[triggers]] entries with provider = "linear" and requires either --team-id or --all-public-teams.

    harn connect linear \
      --url https://example.com/hooks/linear \
      --team-id 72b2a2dc-6f4f-4423-9d34-24b5bd10634a \
      --access-token-secret linear/access-token
    
    harn connect linear \
      --url https://example.com/hooks/linear \
      --all-public-teams \
      --api-key-secret linear/api-key \
      --json
    

    Auth options:

    • --access-token or --access-token-secret
    • --api-key or --api-key-secret

    Use --config <path> to point at an explicit manifest instead of discovering the nearest one from the current working directory.

    harn watch#

    Watch a file for changes and re-run it automatically.

    harn watch main.harn
    harn watch --deny shell main.harn
    

    harn dev#

    Watch a whole Harn project and re-type-check only the modules invalidated by the latest edit. Invalidation is gated by per-module interface fingerprints — a stable BLAKE3 hash of every module's public surface (types, function signatures, pub import re-exports). An edit that only changes a function body leaves the fingerprint alone, so dependents are not re-checked. An edit to a public signature, struct shape, or re-export bumps the fingerprint and every transitive importer is invalidated and re-checked.

    harn dev --watch                 # watch the current directory
    harn dev --watch ./src           # watch a specific tree
    harn dev --watch --json          # NDJSON event stream for agents/editors
    harn dev --watch --with-tests    # also re-run `test_*` pipelines per module
    
    FlagDescription
    --watchRequired. Starts the incremental file-watch loop.
    --jsonEmit a newline-delimited JSON event stream on stdout. Every line is a JsonEnvelope-wrapped event (ready, fingerprint_changed, rerun, diagnostics, tests). See harn --json-schemas --command dev.
    --with-testsAfter each re-check, run every test_* or @test-attributed pipeline in the invalidated modules.
    --test-timeout-ms <ms>Per-module test timeout when --with-tests is set. Defaults to 10000.
    <root>Optional project root. Defaults to the current working directory.

    harn dev --watch --json event shapes (one per line):

    // First event — the initial fingerprint snapshot.
    {"event":"ready","root":"/abs/path","modules":3,"fingerprints":{"src/lib.harn":"<hex>", ...}}
    // Public-surface diff for a single module.
    {"event":"fingerprint_changed","module":"src/lib.harn","old":"<hex>","new":"<hex>"}
    // The set of modules about to be re-checked. Driven by either a body-only
    // edit (just the changed file) or a fingerprint flip (the changed file plus
    // every transitive importer).
    {"event":"rerun","modules":["src/lib.harn","src/user.harn"]}
    // One per module re-checked.
    {"event":"diagnostics","module":"src/user.harn","count":1,"diagnostics":[{"severity":"warning","code":"HARN-ORC-001","message":"Function 'add' expects 3 arguments, got 2","line":2,"column":17}]}
    // Only emitted with `--with-tests`.
    {"event":"tests","module":"src/user.harn","passed":3,"failed":0,"failures":[]}
    

    The bytecode cache (harn precompile) keys on transitive source content, so a fingerprint-stable edit still busts the entry-chunk cache for the changed file. harn dev's contribution is the dependent-pruning half: dependents whose imports' fingerprints didn't move are never re-precompiled.

    harn precompile#

    Compile .harn sources ahead of time so a later harn run of the same source skips parse and compile.

    harn precompile script.harn      # one file
    harn precompile src/             # walk a tree for every .harn file
    harn precompile src/ --out build # write artifacts to a sibling tree instead
    

    Artifacts are written beside each source as <name>.harnbc for an entry chunk and <name>.harnmod for a module, unless --out redirects them.

    FlagDescription
    --out <DIR>Write artifacts under DIR, mirroring the source tree, instead of beside each source.
    --keep-goingContinue after a source fails to compile. The exit code still reports the failure.
    --artifact-contractPrint the machine-readable adjacent-artifact compatibility contract.

    Regenerating artifacts, and what cannot go stale#

    harn precompile is also the command that refreshes these artifacts for a source tree. Re-running it over the same tree rewrites every artifact from current source; deleting the .harnbc and .harnmod files and re-running is equivalent, because a missing artifact is compiled on demand.

    An artifact that no longer matches its source is not used. Both families are found by a key derived from source content, not from a timestamp: an entry chunk by a hash of the entry source, a module artifact by a hash of the module source, each combined with the Harn release and a compiler fingerprint. A candidate whose header does not carry the key being looked up is rejected and the source is compiled instead. So editing a file cannot leave a run executing the previous version of it, and an artifact older than the source beside it is not evidence that the old code ran.

    That is worth stating because the opposite is a natural thing to suspect. If a run appears to ignore an edit, the artifacts are the wrong place to look first; check that the edited file is the one the run actually imports.

    harn portal#

    Launch the local Harn observability portal for persisted runs.

    harn portal
    harn portal --dir runs/archive
    harn portal --manifest examples/personas/harn.toml --persona-state-dir .harn/personas
    harn portal --host 0.0.0.0 --port 4900
    harn portal --open false
    

    See Harn Portal for the full guide.

    harn runs#

    Inspect persisted workflow runs through stable view schemas.

    harn runs view --json .harn-runs/<run>.json
    harn runs view --json --session .harn-runs/
    harn runs report .harn-runs/<run>.json
    harn runs report .harn-runs/<run>.json --events-db .harn/events.sqlite
    harn runs review --report run-report.json --rubric review-rubric.md --model <model>
    harn runs review --run-record .harn-runs/<run>.json --events-db .harn/events.sqlite
    harn runs report --from-session <session-id>
    harn runs report --from-session <session-id> --session-root /path/to/workspace
    

    Reporting on a run that never wrote a run record#

    Nothing on the agent-session path writes a run record: save_run_record is called from std/records, std/workflow, and run_review, so a record exists only when a Harn script asks for one. A host that drives the agent loop directly — an IDE, or a headless agent run — persists every event and no run record, which used to leave every command on this page inapplicable to it.

    --from-session <id> projects the run record from the session Harn already persisted, and every harn runs subcommand accepts it in place of a path. --session-root names the workspace holding .harn/session-store.sqlite, defaulting to the current directory. The projected record is written under .harn-runs/<session-id>.json, so a host can resolve a session's record by id without tracking a path.

    A projected record carries a metadata.projected_from block naming its source, the session's own status, and the fields the projection could not recover: usage.total_duration_ms (session llm_call events record tokens, cost, model, and provider, but no per-call latency — pass --events-db to add that from the event log), policy, and replay_fixture. Workflow-shaped fields — stages, transitions, checkpoints — come back empty because an agent session has none, which is the accurate reading rather than a missing value.

    A session left open by a host that exited without closing it still reports its loop's own terminal verdict, so a finished run is not described as running.

    harn runs report follows the root record's typed child-run pointers and emits the versioned harn.run_report.v1 JSON projection. The report has one row per agent, checked parent/child links, LLM timing and cache/cost data recorded on trace spans, source hashes, redacted visible output, and structural checks. --events-db adds the redacted semantic timeline from a SQLite event log. Canonical subagent_join receipts let the report count terminal children that the parent has not collected and report three separate worst-case intervals: observed_wait_ms from the moment the parent began waiting to the moment it collected, observed_join_ms from the child's terminal state to that same collection, and observed_result_processing_ms for the parent's own result collapse. Keeping them apart is the point — a slow run pays one of scheduler wait, collection lag, or its own work, and one number cannot say which.

    All three remain null, not zero, when the event evidence is absent, malformed, or truncated, and any interval a receipt did not record stays null rather than being back-filled from the join instant. A report that cannot distinguish "the parent never waited" from "the parent waited zero milliseconds" is worse than one that says it does not know. The coordination_timing_unavailable check names only the intervals that are actually missing.

    Timeline snapshots use schema version 2 and carry a coverage object with returned, available, and truncated. available is exact when Harn read every selected source and null when it stopped after proving truncation. A run report with incomplete timeline evidence adds a timeline_truncated warning; consumers must not infer that an event is absent from a truncated snapshot. Increase the timeline query's limit to recover more nodes. The topic cursor remains a subscription watermark, not a continuation token for a snapshot that combines sorted run spans and event topics.

    harn runs review either reads one harn.run_report.v1 file or builds that same report in memory from --run-record, then makes one structured model call. --events-db is available with --run-record; no intermediate report file is required. The command emits harn.run_review.v1 JSON with the verdict, confidence, findings, actions, usage, and lifecycle receipts. Each finding must cite an existing JSON Pointer in the report. The command rejects a changed report hash, an unsupported report schema, and a response with an invalid evidence pointer.

    The default model route selects the available small tier. --model accepts a model alias or provider/model selector. --rubric reads the exact rubric text from a UTF-8 file; otherwise Harn uses its built-in run-review rubric. The output records the resolved provider and model, report and rubric hashes, and an idempotency key derived from those values. Report checks that mark evidence coverage unavailable or incomplete are copied into limitations. The lifecycle.receipts array is embedded in the emitted review artifact; v1 does not append those receipts to an event-log database.

    Before the call, Harn builds a deterministic harn.run_review_evidence.v1 projection. Wide arrays keep bounded first/last samples with their original report pointers; large strings keep bounded previews. Every omission records its original and included size, omitted size, and omitted-content hash. The review provenance records the source and projected byte counts plus the exact projection hash, so this reduction is visible and reproducible. Core report fields such as agents, delegations, LLM calls, coordination, checks, and coverage remain in the projection unless their own collections exceed the same generic bound. Each omission also appears in the review's deterministic limitations array.

    If that bounded projection is still estimated above 48,000 tokens, the command fails before entering the reviewing state. It never silently trims evidence or makes a second model call.

    A run review uses the redacted run report as its only evidence. It does not read raw transcripts, provider-visible prompts, private reasoning, or host UI events. Inspect the report's sources entries and authorized source artifacts when the review names missing evidence. A review is an assessment, not a replacement for the report's deterministic checks.

    Agents can request the deterministic projection through the read-only harn.run.report MCP tool, or build/read and assess it in one call through harn.run.review. The review tool accepts exactly one of report_path or run_record_path; its optional rubric is text rather than another file path. MCP paths are confined to the server's project and state roots; the local CLI accepts an explicit local path. A run review is not marked read-only because it spends a model call, but it is non-destructive and supports MCP task execution.

    Project one authoritative run into a harn.agent_training_example.v1 training example.

    harn runs export-training .harn-runs/<run>.json --json
    harn runs export-training .harn-runs/<run>.json --out example.jsonl
    harn runs export-training .harn-runs/ --run-id <run-id> --session-id <session-id>
    

    The example carries the exact provider-visible message sequence, typed tool-call/result pairs, the tool catalog exactly as it was served to the model, and provenance binding the run id, session id, route, tool format, token usage, terminal status, and the source transcript's SHA-256 and event span.

    The projection is strict. Malformed JSONL, a missing, orphaned, duplicated, or out-of-order tool result, a run without a final no-tool assistant completion turn, an unknown event schema version, a truncated terminal record, a sidecar edited after the run finalized, and a mismatched run or session id each fail with a structured diagnostic instead of being skipped. Pass --run-id to name which run to project when the path is a directory holding several; nothing is selected by output length or a DONE marker.

    harn models lora export consumes the projected example directly, carrying the served catalog through to the trainer rather than inferring one from prompt prose or observed argument values.

    harn replay#

    Replay a persisted workflow run record from saved output, a replay-oracle fixture, or an agent session stored in the SQLite EventLog.

    harn replay .harn-runs/<run>.json
    harn replay --fixture conformance/replay-oracle/fixtures/simple_trigger_local_handler.valid.json --runs 3 --json
    harn replay --fixture fixtures/coding-turn.replay.json --json
    harn replay --session-id <id> --events-db .harn/events.sqlite --runs 3 --json
    harn replay --session-id <id> --events-db .harn/events.sqlite --at 7 --counterfactual ./what-if.harn --json
    

    --session-id reads the observability.agent_events.<sanitized-id> topic from --events-db in append order, reconstructs a replayable run record, and feeds it through the same replay summary path as run-record input. When --runs N is greater than one, JSON output includes per-run reports plus an allowlist-normalized determinism summary. --at <event-id> rehydrates the session prefix through the inclusive event id. Repeat --counterfactual <plan.harn> to chain returned edit-op lists into one cumulative dry-run divergence report. An offline_coding_replay fixture takes the execution path instead of the projection path. It recreates the declared workspace seed, runs the recorded LLM responses with provider and network access disabled, and fails closed on tool order, ids, execution status, final diff, or terminal-outcome divergence. --json uses the universal envelope; the offline receipt is in data, with its producer version and attested source revision when available, exact canonical argument and semantic result digests, cross-run repeatability verdict, and independently observed provider-fixture and process-network isolation facts. Its receipt always lists pending and missing comparison names. harn --json-schemas is the schema authority.

    harn eval#

    Evaluate a persisted workflow run record as a regression fixture, or diff a .harn.prompt template across a fleet of models with harn eval prompt.

    harn eval .harn-runs/<run>.json
    harn eval .harn-runs/<run>.json --compare baseline.json
    harn eval .harn-runs/
    harn eval evals/regression.json
    harn eval harn.eval.toml
    harn eval evals/clarifying-question.json
    harn eval --llm-mock fixtures.jsonl --structural-experiment doubled_prompt pipeline.harn
    harn eval context examples/evals/context-engineering-smoke.json \
        --output target/context-eval --json
    harn eval prompt examples/coding-agent-system.harn.prompt \
        --fleet claude-opus-4-7,gpt-5,gemini-2.5-pro,qwen3.5,ollama:qwen3.5 \
        --bindings examples/coding-agent-system.bindings.json
    

    The legacy harn eval <path> entrypoint accepts five inputs:

    • a single run record JSON file
    • a directory of run record JSON files
    • an eval suite manifest JSON file with grouped cases and optional baseline comparisons
    • an eval-pack v1 TOML/JSON manifest such as harn.eval.toml
    • a standalone persona eval ladder manifest

    Run eval packs declared by a package manifest with:

    harn test package --evals
    

    Package eval discovery uses [package].evals = ["evals/webhooks.toml"] when present, otherwise it falls back to harn.eval.toml in the package root. After harn install, discovery also includes eval packs declared by dependency packages in the leased current generation.

    Clarifying-question evals use an explicit fixture with "eval_kind": "clarifying_question". The fixture checks persisted harness.interaction.ask_user(...) prompts captured in the run record and can enforce a single minimal question via required_terms, forbidden_terms, and question-count bounds.

    When path is a .harn pipeline file, --structural-experiment <spec> runs the pipeline twice in isolated temp run directories: once as the baseline and once with HARN_STRUCTURAL_EXPERIMENT=<spec>. The CLI then evaluates both run sets against their embedded replay fixtures and prints a paired A/B summary. Use --llm-mock <fixture.jsonl> to keep the two runs deterministic.

    harn eval context#

    Run deterministic context-engineering evals over task fixtures and named context modes:

    harn eval context examples/evals/context-engineering-smoke.json \
        --output target/context-eval --json
    

    The manifest uses _type = "harn.context_eval.manifest.v1" and can be JSON or TOML. It declares tasks, artifacts, transcript snippets, tool disclosures, expected terms/artifacts/tools, and modes such as HUD packs, projections, compaction, or limited tool disclosure. The local runner does not call an LLM; it scores whether each mode exposes enough deterministic context for the task and writes:

    • summary.json: a harn.context_eval.report.v1 aggregate report
    • per_run.jsonl: one machine-readable record per task/mode run
    • summary.md: a compact human-readable table

    The report schema is checked into spec/schemas/context-eval-report.v1.schema.json so hosted evaluators, dashboards, and downstream products can ingest the same local artifacts.

    harn eval prompt#

    Render a single .harn.prompt template against every model in a fleet, optionally execute it against each, and surface the resulting wire envelopes side-by-side. This is the cross-model diff renderer that exists so capability-adapted variants do not rot silently as the model mix evolves (see Capability-aware prompts).

    # Render-only: produces side-by-side rendered envelopes per model.
    harn eval prompt examples/coding-agent-system.harn.prompt \
        --fleet claude-opus-4-7,gpt-5,gemini-2.5-pro,qwen3.5,ollama:qwen3.5
    
    # Run mode: also calls each model and collects the response.
    harn eval prompt examples/coding-agent-system.harn.prompt \
        --fleet claude-opus-4-7,gpt-5 \
        --mode run
    
    # Judge mode: scores cross-model equivalence with an LLM judge.
    harn eval prompt examples/coding-agent-system.harn.prompt \
        --fleet claude-opus-4-7,gpt-5,gemini-2.5-pro \
        --mode judge \
        --judge-model claude-opus-4-7
    
    # Use a named fleet from `[eval.fleets.<name>]` in harn.toml.
    harn eval prompt prompts/agent.harn.prompt --fleet-name frontier --output html -o report.html
    
    # Score repo-context artifact selection and rendered section shape.
    harn eval prompt examples/evals/repo-context-quality.harn.prompt \
        --fleet claude-sonnet-5,gpt-5.4-mini,ollama:qwen3.5 \
        --context-fixture examples/evals/context-retrieval-fixture.json \
        --output json -o context-quality.json
    
    FlagDescription
    --fleet <list>Comma-separated model selectors (alias or provider:model). Repeatable.
    --fleet-name <name>Named fleet from [eval.fleets.<name>] in harn.toml.
    --bindings <path>JSON file injected into the template scope. Top-level value must be an object.
    --context-fixture <path>JSON fixture(s) for context-quality gates. Each case supplies candidate artifacts, assembler options, and expectations for selected/rejected/stale artifact ids, token budget, and logical-section envelopes. Repeatable.
    --mode <render|run|judge>Default render.
    --output <terminal|json|html>Default terminal.
    -o, --out-file <path>Write --output payload to a file instead of stdout.
    --max-concurrent <N>Cap concurrent provider invocations in run / judge modes (default 4).
    --judge-template <path>Override the built-in judge .harn.prompt.
    --judge-model <selector>Judge model (default claude-opus-4-7).
    --max-tokens <N>Cap on completion tokens for run / judge calls (default 1024).
    --fail-on-unauthorizedTreat unauthenticated providers as errors instead of skipping.

    Fleets can be declared in harn.toml and reused:

    [eval.fleets.frontier]
    models = ["claude-opus-4-7", "gpt-5", "gemini-2.5-pro"]
    
    [eval.fleets.local]
    models = ["ollama:qwen3.5"]
    

    Render mode never calls a model — it only pushes the LLM render context per fleet member and resolves the template so authors can confirm that {{ if llm.capabilities.* }} branches produce the intended wire envelope on each profile. Run mode synthesizes a thin Harn driver and routes through the existing harness.llm.call infrastructure, so credentials, provider catalog, and HARN_LLM_PROVIDER=mock work exactly as in harn run. Judge mode is run mode plus a final LLM-as-judge call that asks for a one-line JSON verdict on cross-model equivalence.

    --context-fixture adds deterministic repo-context quality gates to the same fleet render. The CLI first packs each case's candidate artifacts with assemble_context, injects context, assembled_context, candidate_artifacts, selected_artifact_ids, and dropped_artifact_ids into the template bindings, then renders the case across every fleet member. JSON/HTML/terminal output includes a context_eval report with selected artifact ids, stale/noisy rejection, budget adherence, per-model logical-section envelopes, and an overall score suitable for CI or dashboard ingestion.

    harn merge-captain#

    Run, audit, or exercise Merge Captain fixtures.

    harn merge-captain run --backend replay examples/personas/merge_captain/transcripts/green_pr.jsonl --once
    harn merge-captain audit examples/personas/merge_captain/transcripts/green_pr.jsonl --golden examples/personas/merge_captain/goldens/green_pr.json
    harn merge-captain ladder personas/merge_captain/harn.eval.toml --format json --report-out .harn-runs/merge-captain-ladder/report.json
    harn merge-captain iterate examples/personas/merge_captain/iterations/smoke.toml --format json
    harn merge-captain iterate --diff .harn-runs/merge-captain-iterations/baseline .harn-runs/merge-captain-iterations/candidate
    

    harn merge-captain ladder runs every configured model route and timeout tier against the same backend fixture. It writes per-tier JSONL transcripts, receipts, and summaries, and emits an aggregate report with the first correct tier plus degraded or looping tiers.

    harn merge-captain iterate runs the next layer up: scenario × variant sweeps for agent-led prompt/package iteration. Each variant can carry model route, timeout tier, Harn package revision, and prompt-asset revision metadata. The iteration directory is self-contained: it stores copied replay fixtures or materialized mock playgrounds, per-run JSONL transcripts, receipts, summaries, summary.json, and summary.md. The aggregate table ranks variants by transcript-drift score, then cost. --diff A B compares two iteration directories or summary JSON files and marks scenario/variant cells as improved, regressed, unchanged, or missing.

    harn orchestrator#

    Long-running manifest-driven orchestrator for trigger ingestion and connector activation. See Orchestrator for full detail.

    # Start the orchestrator against a manifest. Binds HTTP(S) for
    # webhook/a2a-push triggers, activates connectors, writes a state
    # snapshot, drains cleanly on SIGTERM/SIGINT, reloads on SIGHUP.
    harn orchestrator serve \
      --config harn.toml \
      --state-dir .harn/orchestrator \
      --bind 0.0.0.0:8080 \
      --pump-max-outstanding 64 \
      --log-format json \
      --role single-tenant
    
    # Scrape Prometheus metrics from the live listener.
    curl http://127.0.0.1:8080/metrics
    
    # Inspect the running orchestrator state, trigger flow-control state, and recent dispatches.
    harn orchestrator inspect --state-dir .harn/orchestrator
    
    # Inject a synthetic TriggerEvent to exercise a specific binding.
    harn orchestrator fire <trigger-id>
    
    # Replay a historical event through the dispatcher.
    harn orchestrator replay <event-id>
    
    # Run replay determinism oracle fixtures.
    harn orchestrator replay-oracle
    
    # Inspect the dead-letter queue.
    harn orchestrator dlq list
    harn orchestrator dlq --replay <event-id>
    
    # Inspect the pending-queue head.
    harn orchestrator queue
    
    # List worker queues + stranded dispatcher envelopes explicitly.
    harn orchestrator queue --config harn.toml --state-dir ./.harn/orchestrator ls
    
    # Drain one worker queue with a local consumer manifest.
    harn orchestrator queue --config harn.toml --state-dir ./.harn/orchestrator drain <queue>
    
    # Drop ready jobs from a worker queue.
    harn orchestrator queue --config harn.toml --state-dir ./.harn/orchestrator purge <queue> --confirm
    
    # Generate and run a cloud deploy bundle.
    harn orchestrator deploy --provider fly --manifest ./harn.toml --build --dry-run
    

    harn orchestrator inspect/fire/replay/dlq/queue are offline operations — they read the state snapshot + event log directly. To operate against a live harn orchestrator serve, use the same state directory. Environment variables HARN_ORCHESTRATOR_MANIFEST, HARN_ORCHESTRATOR_LISTEN, HARN_ORCHESTRATOR_STATE_DIR, HARN_ORCHESTRATOR_API_KEYS, and HARN_ORCHESTRATOR_HMAC_SECRET configure the serve entry point for container deployments. harn orchestrator deploy accepts --provider render|fly|railway, validates the manifest with the orchestrator runtime, writes provider files under deploy/<provider>/, optionally builds/pushes --image with --build, and syncs locally available secrets unless --no-secret-sync is set.

    harn routes#

    Static trigger-surface inventory for a project manifest.

    # Human-readable table of declared trigger routes.
    harn routes .
    
    # Stable JSON envelope for automation and deployment audits.
    harn routes . --json
    

    harn routes <root> loads the nearest harn.toml, validates the manifest trigger declarations, and reads local handler source without executing it. The report includes trigger kind/provider, HTTP route path when the binding exposes one, local module and handler, match events, declared budgets, statically required host capabilities, vendor-lock disclosure from provider-specific imports, and template framework overhead tokens for non-trivial harness.fs.render_prompt(...) / harness.fs.render_template(...) use. Webhook and A2A-push triggers default to /triggers/<id> when no path is declared.

    harn trigger replay#

    Replay a persisted TriggerEvent from a standalone EventLog snapshot through the dispatcher (no orchestrator needed).

    # Replay an event, re-dispatch against the live binding.
    harn trigger replay <event-id>
    
    # Compare replay result vs. original (structured drift JSON).
    harn trigger replay <event-id> --diff
    
    # Replay against a historical binding version by timestamp.
    harn trigger replay <event-id> --as-of 2026-04-19T12:00:00Z
    
    # Capture a human replay correction for teaching/policy feedback.
    harn trigger replay <event-id> --steer-from outcome --to-decision '{"status":"skipped"}' --reason "human corrected routing" --applied-by alice --scope this_persona
    
    # Preview a filtered bulk replay without dispatching anything.
    harn trigger replay --where "event.payload.tenant == 'acme' AND attempt.status == 'failed'" --dry-run
    
    # Replay matching records with progress output and throttling.
    harn trigger replay --where "attempt.failed_at > '2026-04-18'" --progress --rate-limit 4
    

    Sets HARN_REPLAY=1 during dispatch so nondeterminism in handlers can fall back to recorded values when the handler cooperates. --steer-from records the human's injected decision as a typed correction after a successful single-event replay. The step selector accepts event, outcome, or an action-graph node id. this_persona and all correction scopes tighten derived capability policy for the affected actor until the matching correction records no longer apply.

    Bulk replay selection uses a Harn expression over event-log records with top-level event, binding, attempt, outcome, and audit objects. The CLI accepts SQL-ish convenience syntax for filters: single-quoted strings plus AND/OR/NOT normalize into the underlying Harn expression evaluator before dispatch.

    --dry-run returns the matching records without replaying them. --progress streams per-item progress to stderr, and --rate-limit caps bulk execution throughput in operations per second.

    Every bulk replay appends an audit envelope to trigger.operations.audit describing who ran it, when, the normalized filter, and the affected records.

    harn flow replay-audit#

    Audit shipped Flow slices against the current @retroactive predicate hashes. Historical slices remain append-only: drift is advisory unless --fail-on-drift is set. The --store path must already exist; the audit command does not create an empty Flow store.

    harn flow replay-audit --since 2026-04-26
    harn flow replay-audit --since 2026-04-26T12:00:00Z --json
    harn flow replay-audit --store .harn/flow.sqlite --predicate-root . --touched-dir crates/harn-vm --since 2026-04-26 --fail-on-drift
    
    FlagDescription
    --since <date>Include shipped derived slices created at or after an RFC3339 timestamp, unix timestamp, or YYYY-MM-DD
    --store <path>SQLite Flow store to audit (default: .harn/flow.sqlite)
    --root <path>Repository root used for invariants.harn discovery
    --target-dir <path>Directory whose effective current predicate set is audited
    --fail-on-driftExit non-zero when advisory drift is found
    --jsonEmit the replay-audit report as JSON

    harn trace import#

    Convert a third-party eval trace into a standard --llm-mock fixture.

    harn trace import \
      --trace-file traces/generic.jsonl \
      --trace-id trace_123 \
      --output fixtures/imported.jsonl
    

    The source file is JSONL. Each line should contain at least {prompt, response} and may also include tool_calls, model, provider, token counts, and trace_id. The generated fixture can be used directly with harn run --llm-mock ..., harn eval --llm-mock ..., or harn test --determinism.

    harn trace prefix-stability#

    Check whether each captured provider request keeps the prior request's prompt prefix unchanged:

    harn trace prefix-stability .harn-runs/<run-id>-llm --json
    

    The directory must contain llm_transcript.jsonl and the raw-provider/ files written when HARN_LLM_TRANSCRIPT_RAW=1 is set. The command checks each consecutive request pair. Prior messages must remain byte-for-byte equal and in the same positions. The system block and ordered tool list must also remain equal. New messages may only be appended.

    The command exits with status 1 when a prefix changes. Its report names the first changed message's index and role, then shows the values before and after the change. --json emits a harn.trace.prefix_stability.v1 report.

    harn crystallize#

    Mine repeated traces into a reviewable deterministic workflow candidate.

    harn crystallize \
      --from fixtures/crystallize/version-bump \
      --shadow-from fixtures/crystallize/version-bump-holdout \
      --out workflows/version_bump.harn \
      --report reports/version_bump.crystallize.json \
      --eval-pack evals/version_bump.toml \
      --min-examples 5 \
      --workflow-name version_bump
    

    The input directory may contain crystallization trace JSON files or persisted Harn workflow run records. The report preserves source trace hashes, parameters, side effects, approval points, capability and secret requirements, shadow-mode pass/fail details, promotion metadata, and cost/token savings. Pass --shadow-from <TRACE_DIR> to add future/holdout traces to the shadow comparison without using them for mining. Candidates with divergent side effects or replay-oracle receipt drift are rejected instead of promoted.

    Pass --bundle <DIR> to also emit a portable harn.crystallization.candidate.bundle directory (candidate.json, workflow.harn, report.json, harn.eval.toml, redacted fixtures/) that a cloud platform or any other importer can consume without bespoke glue:

    harn crystallize \
      --from fixtures/crystallize/version-bump \
      --out workflows/version_bump.harn \
      --report reports/version_bump.crystallize.json \
      --eval-pack evals/version_bump.toml \
      --bundle bundles/version-bump \
      --bundle-team platform \
      --bundle-repo burin-labs/harn \
      --bundle-risk-level medium \
      --workflow-name version_bump
    

    harn crystallize validate#

    Smoke-check a bundle on disk: confirms the schema marker and version, that every referenced file exists, that fixtures are marked redacted, and that required_secrets only lists logical ids (never raw secret values).

    harn crystallize validate bundles/version-bump
    

    harn crystallize shadow#

    Re-run the deterministic shadow comparison from a bundle's redacted fixtures in-process, with no live side effects. Returns non-zero on divergence, so it fits in CI gates.

    harn crystallize shadow bundles/version-bump
    

    See Workflow crystallization for the trace schema, bundle layout, and review loop.

    The checked-in release/package-maintenance harness can be run with:

    harn crystallize \
      --from crates/harn-vm/tests/fixtures/crystallize_v2_release/mine \
      --shadow-from crates/harn-vm/tests/fixtures/crystallize_v2_release/holdout-pass \
      --out /tmp/release_package_maintenance.harn \
      --report /tmp/release_package_maintenance.report.json \
      --bundle /tmp/release-package-maintenance \
      --min-examples 3 \
      --workflow-name release_package_maintenance \
      --package-name release-workflows \
      --approver release-lead@example.com
    

    harn trigger cancel#

    Request cancellation for pending or in-flight non-replay trigger dispatches recorded in the EventLog snapshot.

    # Cancel a single event/binding lineage if it is still active.
    harn trigger cancel <event-id>
    
    # Preview which active runs match a filter.
    harn trigger cancel --where "attempt.handler == 'handlers::risky'" --dry-run
    
    # Request cancellation for matching runs with progress output.
    harn trigger cancel --where "event.payload.tenant == 'acme'" --progress --rate-limit 4
    

    Cancellation writes durable control records to trigger.cancel.requests and the dispatcher polls that topic while dispatches are queued, sleeping between retries, or running local handlers. Terminal runs are reported as not_cancellable and are left unchanged.

    harn trust query#

    Query trust-graph records from the workspace event log.

    # List all trust records for one agent.
    harn trust query --agent github-triage-bot
    
    # Filter by action, tier, and outcome, then emit JSON.
    harn trust query \
      --agent github-triage-bot \
      --action github.issue.opened \
      --tier act-auto \
      --outcome success \
      --json
    
    # Aggregate per-agent stats.
    harn trust query --summary
    

    Supported filters:

    • --agent
    • --action
    • --since
    • --until
    • --tier
    • --outcome
    • --json
    • --summary

    --summary groups records by agent and reports success rate, mean recorded cost, tier distribution, and outcome distribution.

    harn trust verify-chain#

    Verify the workspace trust graph's hash chain.

    harn trust verify-chain
    harn trust verify-chain --json
    

    The command reads trust_graph and falls back to legacy trust.graph logs, recomputes every entry_hash, and checks that each previous_hash points to the prior record.

    harn trust promote#

    Manually promote an agent to a higher autonomy tier. This appends a trust.promote control record to the trust graph.

    harn trust promote github-triage-bot --to act-auto
    harn trust promote reviewer-bot --to act-with-approval
    

    harn trust demote#

    Manually demote an agent and record the reason in trust-graph metadata.

    harn trust demote github-triage-bot --to shadow --reason "unexpected mutation"
    harn trust demote deploy-bot --to suggest --reason "needs tighter review gate"
    

    harn serve#

    Start a workflow server through one of the outbound transport adapters.

    harn serve a2a agent.harn                  # explicit A2A
    harn serve agent.harn                      # legacy A2A shorthand
    harn serve a2a --port 3000 agent.harn      # A2A with custom port
    harn serve a2a --bind 0.0.0.0:3000 --api-key "$HARN_SERVE_API_KEY" agent.harn
    harn serve acp agent.harn                  # ACP session server over stdio
    harn serve acp --profile-json /tmp/acp.ndjson agent.harn
    harn serve api agent.harn                  # local OpenAPI + SSE Agents API
    harn serve api --bind 127.0.0.1:8787 agent.harn
    harn serve mcp server.harn                 # exported pub fn -> MCP tools over stdio
    harn serve mcp --transport http server.harn
    harn serve worker jobs.harn                # scheduled and queued @job functions
    harn serve worker --tenant acme jobs.harn  # one active tenant's jobs
    harn serve test                            # reusable user-test worker over stdio
    

    harn serve worker and harn run --as-job load connectors declared by the nearest harn.toml. A job can call them through harness.net.connector_call(...). The worker resolves each connector's declared credentials through the same secret providers used by direct Harn runs. Startup reports a connector load or initialization error before it accepts work; a missing credential remains a call error from that connector. Pass --tenant ID to bind every worker dispatch to an active tenant in the local tenant registry. The same option works with harn run --as-job. Job code and connector clients then share that tenant's secret scope, and harness.tenant.id() returns the bound ID. An unknown or suspended tenant stops startup. A request for another tenant's secret is denied; it is not reported as a missing credential. Without --tenant, the existing single-tenant secret namespace remains active, and the worker only claims jobs that have no tenant ID. Tenanted work stays queued for a matching worker. The default registry is <script-dir>/.harn/orchestrator; pass --tenant-state-dir PATH or set HARN_ORCHESTRATOR_STATE_DIR when the orchestrator uses another location.

    harn serve test lets a caller retain one isolated test-run session instead of spawning a fresh Harn process for every suite. It accepts JSON-RPC 2.0 as newline-delimited JSON or Content-Length frames. Call initialize with {"protocol_version":"1"}; its result includes the negotiated protocol and the Harn server_version, stable worker_id, and process_id. Then call test/run with a test path and any of filter, timeout_ms, max_test_ms, max_execute_ms, parallel, fail_fast, jobs, shard, skill_dirs, or diagnose. Each response includes the same worker identity, typed test summary, cumulative run_count, and cache counters before and after that run. The advertised test_run.schema_version is 2; summaries include the shared duration distribution plus per-case and aggregate module attribution. shutdown returns the final run and cache receipt; closing stdin also stops the worker. Every test still receives fresh VM and module state; only reusable prepared module artifacts are retained by the worker session.

    harn serve api starts the local Harn Agents HTTP API. It serves /openapi.json, health/version/runtime/capability metadata, session and task CRUD, SSE event streams, local tool-registry inspection, workspace file helpers, and permission request response endpoints. Prompt execution, cancellation, and approval decisions are routed through the packaged ACP session runtime so API clients share the same transcript, EventLog, replay, and host-permission paths as ACP hosts. Use --api-key <key> / HARN_SERVE_API_KEY, --hmac-secret <secret> / HARN_SERVE_HMAC_SECRET, and the shared --tls flags to protect non-discovery routes.

    harn serve mcp uses the shared harn-serve dispatch core and maps each exported pub fn in the target module to one MCP tool. Tool schemas are derived from Harn type annotations. With --transport http, the server also supports stable Streamable HTTP POST requests on --path.

    ACP and MCP stdio transports reserve stdout for JSON-RPC frames, so they reject --obs stdout. Use --obs stderr, --obs otel, or --obs off instead. Stdout observability remains available with MCP HTTP and ACP WebSocket, where stdout is not the protocol transport.

    For scripts that author the MCP surface through the registration builtins (harness.tools.mcp_tools(registry), harness.tools.mcp_resource(...), harness.tools.mcp_prompt(...)) instead of pub fn exports, harn serve mcp auto-detects that mode, runs the script once, and exposes the registered tools / resources / prompts over either stdio or Streamable HTTP. Pass --card <PATH_OR_JSON> to advertise an MCP v2.1 Server Card.

    harn serve mcp agent.harn                  # auto-detect surface
    harn serve mcp agent.harn --card ./card.json
    harn serve mcp --surface script --watch agent.harn
    

    --watch is available for a script-published registry over stdio. Harn loads and validates the complete replacement before swapping handlers, keeps the previous runtime after an invalid edit, and notifies the connected client with the standard tool, resource, and prompt list-change notifications after a successful reload.

    harn serve a2a uses the shared harn-serve dispatch core and exposes each exported pub fn in the target module as an A2A skill. The adapter publishes an AgentCard at /.well-known/agent-card.json, keeps the legacy /.well-known/a2a-agent, /.well-known/agent.json, and /agent/card aliases, and supports task send, send-and-wait, streaming/resubscribe, push callback registration, and cancel propagation. The legacy shorthand harn serve <file> is preserved and rewrites internally to harn serve a2a <file>. The listener binds to 127.0.0.1:8080 by default; use --bind 0.0.0.0:PORT only with explicit auth and TLS or a trusted edge proxy.

    harn serve acp starts the packaged ACP adapter on stdio for editor and IDE hosts. Pass --transport websocket to expose the same single-connection ACP session server at --bind and --path instead. It creates ACP sessions, executes the target pipeline for each session/prompt, streams AgentEvent values as session/update notifications, and forwards permission prompts through session/request_permission. Use --api-key <key> / HARN_SERVE_API_KEY or --hmac-secret <secret> / HARN_SERVE_HMAC_SECRET to advertise ACP authMethods and require authenticate before protected session methods. WebSocket clients can also pre-authenticate the upgrade with Authorization: Bearer <key> or X-API-Key. Use --profile / HARN_PROFILE=1 to print one categorical timing rollup per executed session/prompt; use --profile-json <path> / HARN_PROFILE_JSON=<path> to append per-turn NDJSON records with turn, session_id, and rollup. File-backed ACP prompts report vm_setup as a profile bucket and reuse a prepared VM baseline while preserving clean per-turn execution state. --trace / HARN_TRACE=1 enables the LLM trace summary printed when the stdio server shuts down.

    See MCP and ACP Integration and Outbound workflow server for protocol details.

    harn mcp#

    Manage standalone OAuth state for remote HTTP MCP servers.

    harn mcp redirect-uri
    harn mcp login notion
    harn mcp login https://mcp.notion.com/mcp
    harn mcp login my-server --url https://example.com/mcp --client-id <id> --client-secret <secret>
    harn mcp status notion
    harn mcp logout notion
    

    harn mcp login resolves the server from the nearest harn.toml when you pass an MCP server name, or uses the explicit URL when you pass --url or a raw https://... target. The CLI:

    • discovers OAuth protected resource and authorization server metadata
    • prefers pre-registered client_id / client_secret values when supplied
    • falls back to dynamic client registration when supported by the server
    • tries the requested loopback port first and uses an OS-selected port only when the selected client-registration mode permits port variance
    • stores tokens in the local OS keychain and refreshes them automatically

    harn mcp status prints the configured server state. For OAuth-backed HTTP servers with a vetted identity descriptor and captured token-response metadata, human output includes the connected account/workspace and --json includes display_identity. Harn scripts see the same field through harn.mcp.status(), alongside the server transport and url.

    Relevant flags:

    FlagDescription
    --url <url>Explicit MCP server URL when logging in/out by a custom name
    --client-id <id>Use a pre-registered client ID instead of dynamic registration
    --client-secret <secret>Optional client secret for client_secret_post / client_secret_basic servers
    --scope <scopes>Override or provide requested OAuth scopes
    --redirect-uri <uri>Override the default loopback redirect URI (default shown by harn mcp redirect-uri)

    For an exact-match preregistered client (auth.mode = "byo"), Harn never silently changes a fixed redirect port. CIMD native clients may vary the loopback port under RFC 8252. Dynamic registration is deliberately repeated for each authorization, so the registration, authorization request, and token exchange all carry the same effective URI after a fixed-port fallback or reauthorization on a new port. Redirect compatibility errors include that URI and the selected client mode.

    Security guidance:

    • prefer the narrowest scopes the remote MCP server supports
    • treat configured client_secret values as secrets
    • review remote MCP capabilities before using them in autonomous workflows

    Maintainer release scripts#

    Harn's repo-maintainer release scripts are documented in Maintainer release workflow. They are not part of the end-user harn CLI surface.

    harn add#

    Add a dependency to harn.toml.

    harn add github.com/burin-labs/harn-openapi@v1.2.3
    harn add @burin/notion-sdk@1.2.3
    harn add @burin/notion-sdk@1.2.3 --registry ./harn-package-index.toml
    harn add https://github.com/user/my-lib --alias my-lib --tag v1.2.3
    harn add https://github.com/user/my-lib --alias my-lib --rev v1.2.3
    harn add https://github.com/user/my-lib --alias my-lib --branch main
    harn add my-lib --git https://github.com/user/my-lib --tag v1.2.3   # legacy form
    

    Git dependencies must specify a stable tag/rev or an explicit branch. harn.lock records the resolved tag when present, commit, and content hash used for reproducible installs. Registry-name dependencies resolve through the package index and then write the same git dependency shape as direct GitHub installs; hand-authored manifests may also use registry semver ranges such as my-lib = { version = "^1.2" }.

    Git locks use the platform-independent sha256-v2:<hex> tree identity in harn.lock version 5. Paths, text line endings, permissions, and symlink handling follow the canonical package projection defined in the language spec. To migrate a version 4 lock, run harn install and commit the rewritten lock. Commands such as harn check, harn lint, and harn run can read a version 4 lock. They build a version 5 package snapshot in memory and leave harn.lock unchanged. Locked and offline installs reject version 4 Git hashes because those modes cannot change the committed lock.

    harn install#

    Install dependencies declared in harn.toml, writing or reusing harn.lock and atomically publishing direct plus transitive dependencies as an immutable package generation.

    harn install
    harn install --frozen
    harn install --locked --offline
    harn install --refetch my-lib
    harn install --json
    

    --locked is a CI-oriented alias for --frozen: Harn fails if harn.toml and harn.lock disagree. --offline also implies locked behavior and fails instead of fetching when a locked git package is missing from the shared cache. --json emits a structured install summary suitable for IDE-host and cloud-platform automation.

    harn lock#

    Resolve dependencies from harn.toml and write harn.lock without materializing packages.

    harn lock
    

    harn update#

    Refresh one dependency (or all of them) and update harn.lock.

    harn update my-lib
    harn update --all
    harn update --all --json
    

    --json emits a structured update summary, mirroring harn install.

    harn remove#

    Remove one dependency from harn.toml and harn.lock, then publish a new generation without that package.

    harn remove my-lib
    

    harn pack#

    Build a .harnpack run bundle from a Harn entrypoint (#1781). The bundle is a deterministic tar.zst archive containing a v2 WorkflowBundle manifest (harnpack.json), every transitively-imported .harn source and non-Harn import asset under sources/, every module's precompiled .harnbc/.harnmod artifact under bytecode/, a provider-catalog hash + stdlib version pin, an archived SPDX-lite 2.3 SBOM (sbom.spdx.json), and an optional Ed25519 signature slot.

    harn pack examples/hello.harn
    harn pack examples/hello.harn --out /tmp/hello.harnpack
    harn pack examples/hello.harn --sign --key ~/.harn/release-ed25519.pem
    harn pack examples/hello.harn --unsigned --json
    harn pack workflows/new-entry.harn --upgrade old.harnpack --out new.harnpack
    

    Output paths default to <entrypoint>.harnpack next to the entrypoint. --json emits a JsonEnvelope with bundle_hash (BLAKE3 over the canonical manifest bytes plus sorted content hashes), output_path, size_bytes, signature status, SBOM counts, bytecode/debug-symbol metadata, and the full manifest. Bytecode metadata reports counts and bytes separately for .harnbc entry chunks and .harnmod module artifacts, plus their summed total_bytes; see the catalog row at harn --json-schemas --command pack for the schema version and inline JSON Schema. Repacking the same source produces a byte-identical archive.

    harn run <bundle.harnpack> verifies the archive, then projects each generated artifact beside its source inside the content-addressed replay cache. This reuses the same validating loaders as ordinary adjacent precompiled artifacts; source, context, runtime-version, or compiler-build mismatches recompile instead of executing stale bytecode. The archive itself remains unchanged, with one copy under bytecode/ for deterministic verify, unpack, and repack behavior. Every cache hit is rechecked against the verified archive and repaired atomically if its manifest, source, asset, or artifact bytes changed. Signed legacy bundles also require every runtime payload path to be bound by a module manifest entry or SBOM asset row.

    --upgrade <old.harnpack> reads a prior bundle (v1 JSON or v2 archive) and re-emits it under the v2 manifest, preserving the prior bundle's id, name, version, triggers, workflow graph, and prompt capsules. The new <entrypoint> argument supplies the transitive-module + SBOM payload that the older schema lacked.

    --sign --key <path> loads an Ed25519 PKCS#8 PEM private key, signs the canonical bundle hash, embeds the signature in the manifest, and appends an OpenTrustGraph release record. --unsigned skips the manifest signature but still appends the release record at autonomy tier suggest; this is also the default when neither signing flag is provided.

    --exclude-secrets refuses secret-looking entrypoints and skips imported non-Harn assets whose paths match .env, .env.*, *.pem, *.key, credentials*, or a secrets/ directory. Skipped assets are reported as JsonEnvelope.warnings entries and in manifest.metadata.skipped_assets.

    harn package list#

    List packages from the current harn.lock.

    harn package list
    harn package list --json
    

    The report includes each locked package's source, materialization status, integrity status, package version, Harn compatibility range, exported modules/tools/skills, permissions, and host requirements.

    harn package doctor#

    Diagnose package install and contract issues.

    harn package doctor
    harn package doctor --json
    

    Doctor checks for missing or stale lockfiles, missing materialized packages, content-hash mismatches, declared host capability gaps, and invalid installed package tool/skill metadata. Publish-readiness checks remain under harn package check, so applications can use doctor without adding package metadata that only published libraries need.

    harn package verify#

    Run the complete package CI contract and emit a versioned receipt.

    harn package verify
    harn package verify --json
    harn package verify --strict
    harn package verify --receipt-out .harn/receipts/package-verify.json
    harn package verify . --provider github --run-poll-tick
    

    The verifier performs a locked install; validates package metadata; checks, lints, and format-checks package-owned Harn files; runs package tests; installs the package into a temporary consumer and checks each export; validates documentation and generated API docs; and dry-runs package packing. Connector packages also run their connector metadata and deterministic fixture contract. Each schema-v2 receipt check records whether the gate was applicable and reached, plus its command, result, timing, and diagnostics. Package test discovery has a separate read-only command and receipt.

    harn package test-inventory#

    Inspect package test discovery without executing tests or changing package files. Release compatibility scans use this command with the candidate Harn runtime so a new discovery rule fails before publication reaches downstream bump pull requests.

    harn package test-inventory .
    harn package test-inventory . --json
    harn package test-inventory . --json \
      --receipt-out .harn/receipts/package-test-inventory.json
    

    The command exits nonzero for an unreasoned empty suite, a parse or discovery error, or any selected file with no discoverable test pipeline. Its schema-v1 receipt reports selected-file and discovered-test counts, the configured empty suite exception, and path plus SHA-256 identity for each file with no test or with a discovery error. It does not enumerate files that passed discovery.

    --strict makes both check and lint warnings fatal and enables strict boundary type checking. Schema-v2 receipts expose strict_requested and record the exact strict command for each source gate; manifest-level [check] policy is still applied monotonically by those commands.

    harn package scaffold openapi#

    Create a focused Harn SDK package from a local or remote OpenAPI 3.1 spec.

    harn package scaffold openapi \
      --name acme-sdk-harn \
      --module-name acme_sdk \
      --client-name AcmeClient \
      --spec ./openapi.json \
      --out ./acme-sdk-harn
    

    The scaffold writes harn.toml, generated src/lib.harn, the source spec under openapi/, scripts/regen.harn, tests/smoke.harn, docs, README, license, and package CI. Run harn install inside the package before checking or testing so the declared harn-openapi regeneration dependency is materialized.

    Useful flags:

    FlagDescription
    --default-base-url <url>Override the first servers[].url from the spec
    --harn-openapi-path <dir>Use a local harn-openapi checkout for generation
    --harn-openapi-git <url-or-path>Dependency source written to harn.toml
    --harn-openapi-rev <rev>Pin the dependency to a rev or tag
    --harn-openapi-branch <branch>Track a branch instead of the default pinned rev
    --forceOverwrite generated files in an existing output directory

    Search the configured package registry index.

    harn package search notion
    harn package search --registry ./harn-package-index.toml --json
    

    The registry source comes from --registry, HARN_PACKAGE_REGISTRY, [registry].url in harn.toml, or Harn's default hosted index. Private HTTP(S) registries can be accessed with HARN_PACKAGE_REGISTRY_TOKEN; Harn sends it as a bearer token when fetching the index and any archive URLs listed by that index.

    Registry versions may resolve to either a git source or an archive source. Archive versions use archive = "<url>" plus checksum = "sha256:<tree>"; Harn expands .tar.gz archives into the package cache and verifies the expanded tree hash before writing harn.lock.

    harn package registry verify#

    Validate registry v2, immutable content identities, and repository-bound provenance through the same parser used by install, search, and info.

    harn package registry verify ./harn-package-index.toml --json
    harn package registry verify ./harn-package-index.toml --remote \
      --receipt-out registry-verification.json
    

    --remote additionally resolves every published Git tag and fails if it no longer points at the full commit SHA recorded in rev, then reads harn.toml at that commit and fails if it declares a different package name or version than the entry advertises. A tag can resolve to exactly the right commit while that commit sells a different version, so tag identity alone does not prove the entry. Yanked versions are skipped: resolution already refuses them, so the registry makes no live claim about their source. The JSON output and optional receipt file use harn.package_registry_verification.v1 and report resolved_git_versions and verified_manifests.

    Publish and discover structural rule packs through the package registry.

    harn rule publish ./acme-rules --registry-name @acme/rules --dry-run
    harn rule search typescript
    harn rule search --registry ./harn-package-index.toml --json
    

    harn rule publish is a rule-pack alias over harn publish: it requires the package manifest to declare [rules] ruleDirs, validates the rule files, and adds rule-pack metadata to the package-index row. harn rule search filters the package registry to rule packs and reports the pack description, languages, rule count, and safety summary.

    harn package info#

    Show registry metadata for one package, optionally at a specific version.

    harn package info @burin/notion-sdk
    harn package info @burin/notion-sdk@1.2.3 --json
    

    Metadata includes repository, license, Harn compatibility, exported modules, connector contract compatibility, docs URL, versions, and any checksum/provenance fields present in the index.

    harn package cache#

    Inspect and maintain the shared git package cache.

    harn package cache list
    harn package cache verify
    harn package cache verify --materialized
    harn package cache clean
    harn package cache clean --all
    

    verify recomputes cached package content hashes and compares them with harn.lock; --materialized also checks the leased current generation. clean removes cache entries not referenced by the current lockfile, while --all clears all cached git package entries.

    harn package outdated#

    Compare the resolved entries in harn.lock against newer registry versions or upstream branch HEADs.

    harn package outdated
    harn package outdated --remote
    harn package outdated --json
    

    Registry-attributed dependencies (added via harn add @scope/name@version) are checked against the configured registry index; pass --registry to override or --refresh to bypass any local cache. Plain git dependencies report skipped unless --remote is set, in which case Harn shells out to git ls-remote to detect branch HEAD drift. Path dependencies always report skipped because they live-link.

    harn package audit#

    Audit harn.lock provenance, package compatibility, and supply-chain integrity in one pass.

    harn package audit
    harn package audit --json
    harn package audit --skip-materialized
    

    The audit verifies that:

    • The lock generator/protocol-artifact versions match the running Harn.
    • Each entry carries provenance (package_version, manifest_digest).
    • Resolved packages still satisfy their harn compatibility range.
    • Cached and materialized git packages still hash to the lockfile entry.
    • The materialized package's harn.toml digest still matches the lock.
    • Registry-attributed entries have not been yanked from the index.
    • Publishable manifests are not pinned to path dependencies.

    Each finding has a stable code (lockfile-stale, harn-compat-violation, yanked-registry-version, etc.) so downstream automation can route specific failure modes without parsing the human report.

    harn package artifacts#

    Inspect or verify the published Harn protocol-artifact contract.

    harn package artifacts manifest
    harn package artifacts manifest --output vendor/manifest.json
    harn package artifacts check vendor/manifest.json
    harn package artifacts check vendor/manifest.json --json
    

    manifest prints (or writes) the protocol-artifact manifest the running Harn binary would emit — the same JSON that make gen-protocol-artifacts ships under spec/protocol-artifacts/manifest.json. check compares a vendored copy of that manifest against the running Harn and reports any drift. Hosts that vendor Harn protocol bindings (IDE hosts, cloud platforms, custom integrators) call check from CI to detect when a Harn bump would require regenerating their bindings.

    harn version#

    Show version information.

    harn version