No description
  • Rust 99.4%
  • JavaScript 0.3%
  • Shell 0.2%
Find a file
Alexander Wollan 61896d410c
All checks were successful
CI / check (push) Successful in 3m28s
fix(M002/S07/T01): add clippy+rustfmt components to rust-toolchain step
2026-04-01 09:58:40 -05:00
.assay feat(M002/S02): Real Assay Binary + Production Wiring 2026-03-17 16:43:36 -05:00
.bg-shell feat(M001/S01): Scaffold, Manifest & Dry-Run CLI 2026-03-17 10:21:46 -05:00
.cargo [kata/root/M011/S01] Decompose manifest.rs and git/cli.rs (#37) 2026-03-27 08:46:13 -05:00
.claude feat(M001/S01): Scaffold, Manifest & Dry-Run CLI 2026-03-17 10:21:46 -05:00
.forgejo/workflows fix(M002/S07/T01): add clippy+rustfmt components to rust-toolchain step 2026-04-01 09:58:40 -05:00
.githooks chore: add git hooks (pre-commit, pre-push) and fix clippy warnings 2026-03-24 17:02:19 -05:00
.github/workflows chore: disable CI workflow for Forgejo mirror (rename to .disabled) 2026-03-29 15:51:10 -05:00
.kata [kata/root/M013/S01] Event Ingestion Foundation (#48) 2026-03-28 12:27:47 -05:00
.planning [kata/root/M006/S02] Directory Watch + HTTP API (#22) 2026-03-23 13:16:55 -05:00
crates feat(M014/S01): PeerUpdate Alignment & HTTP Signal Delivery 2026-03-30 08:15:14 -05:00
examples [kata/root/M012/S05] Dispatch Integration, State Backend Passthrough & Final Assembly (#47) 2026-03-28 08:43:46 -05:00
.envrc chore: add .envrc and enable Linear workflow mode 2026-03-28 09:03:27 -05:00
.gitignore [kata/root/M006/S02] Directory Watch + HTTP API (#22) 2026-03-23 13:16:55 -05:00
Cargo.lock [kata/root/M013/S03] feat(S03): Signal Delivery to Running Sessions (#49) 2026-03-29 12:21:41 -05:00
Cargo.toml [kata/root/M013/S03] feat(S03): Signal Delivery to Running Sessions (#49) 2026-03-29 12:21:41 -05:00
justfile [kata/root/M011/S01] Decompose manifest.rs and git/cli.rs (#37) 2026-03-27 08:46:13 -05:00
README.md test: verify Forgejo push mirror to GitHub (S01/T03) 2026-03-29 16:08:57 +00:00

Smelt

Containerized job execution engine for agentic development workflows.

Smelt is the infrastructure layer in the Smelt / Assay / Cupel toolkit. It provisions isolated Docker (or Compose, or Kubernetes) environments, mounts the host repo, delegates orchestration to Assay inside the container, streams gate output to the terminal, collects the result branch, and creates a GitHub PR for human review.

The output of smelt run is a pull request, ready for review.


Install

Build and install from source (requires Rust):

cargo install --path .

Or build without installing:

cargo build --release
# binary at target/release/smelt

Quickstart

# 1. Generate a skeleton manifest in the current directory
smelt init

# 2. Edit the generated job-manifest.toml — set the image, sessions, and credentials

# 3. Validate the manifest and preview the execution plan
smelt run job-manifest.toml --dry-run

# 4. Run the job (provisions container, runs Assay, collects results)
smelt run job-manifest.toml

See examples/ for complete manifest samples covering Docker, Compose, and Kubernetes runtimes.


Subcommands

smelt init

Generate a skeleton job manifest in the current directory.

Usage: smelt init

Options:
  -h, --help     Print help
  -V, --version  Print version

Example:

smelt init
# Creates job-manifest.toml with a commented template

smelt list

List past runs recorded in .smelt/runs/.

Usage: smelt list [OPTIONS]

Options:
      --dir <DIR>  Directory to search for `.smelt/runs/` (defaults to current directory) [default: .]
  -h, --help       Print help
  -V, --version    Print version
Flag Description
--dir <DIR> Directory to search for .smelt/runs/ (defaults to .)

Example:

smelt list
smelt list --dir /path/to/project

smelt run

Run a job manifest — provisions the container environment, executes Assay sessions, collects results, and optionally creates a PR.

Usage: smelt run [OPTIONS] <MANIFEST>

Arguments:
  <MANIFEST>  Path to the job manifest TOML file

Options:
      --dry-run  Validate and print the execution plan without running anything
      --no-pr    Skip PR creation even when a `[forge]` section is present in the manifest
  -h, --help     Print help
  -V, --version  Print version
Flag Description
--dry-run Validate and print the execution plan without running anything
--no-pr Skip PR creation even when a [forge] section is present in the manifest

Examples:

# Dry run — validate and preview
smelt run examples/job-manifest.toml --dry-run

# Full run
smelt run job-manifest.toml

# Run without creating a PR
smelt run job-manifest.toml --no-pr

smelt serve

Start the job dispatch daemon. Watches a directory for incoming manifest files, accepts jobs via HTTP API, dispatches up to max_concurrent parallel jobs, auto-retries failures, and displays a live TUI dashboard.

Usage: smelt serve [OPTIONS] --config <CONFIG>

Options:
  -c, --config <CONFIG>  Path to the server configuration TOML file
      --no-tui           Disable the Ratatui TUI (tracing output stays on stderr)
  -h, --help             Print help
  -V, --version          Print version
Flag Description
-c, --config <CONFIG> (Required) Path to the server configuration TOML file
--no-tui Disable the Ratatui TUI (tracing output stays on stderr)

Example:

smelt serve --config server.toml
smelt serve --config server.toml --no-tui

See Server Mode below for configuration details.

Health Check

The server exposes GET /health for load-balancer probes and uptime monitors. This endpoint is unauthenticated — it responds even when [auth] is configured, so external health checks don't need a token.

curl http://localhost:3000/health
# {"status":"ok"}

A 200 response means the server is running and accepting connections. Any other status (or a connection refusal) indicates the server is down or not yet ready.


smelt status

Show status of a running or completed job.

Usage: smelt status [OPTIONS] [JOB_NAME]

Arguments:
  [JOB_NAME]  Job name to read (reads per-job state from `.smelt/runs/<job-name>/state.toml`).
              Omit to read legacy flat state for backward compat

Options:
      --dir <DIR>  Path to the project root directory (defaults to current directory) [default: .]
  -h, --help       Print help
  -V, --version    Print version
Flag Description
--dir <DIR> Path to the project root directory (defaults to .)

Example:

smelt status add-user-auth
smelt status add-user-auth --dir /path/to/project

smelt watch

Watch a PR until it is merged or closed. Polls the forge API at a configurable interval and exits with code 0 on merge, 1 on close.

Usage: smelt watch [OPTIONS] <JOB_NAME>

Arguments:
  <JOB_NAME>  Job name to watch (must match job.name in the manifest used for smelt run)

Options:
      --interval-secs <INTERVAL_SECS>  Polling interval in seconds [default: 30]
  -h, --help                           Print help
  -V, --version                        Print version
Flag Description
--interval-secs <INTERVAL_SECS> Polling interval in seconds (default: 30)

Example:

smelt watch add-user-auth
smelt watch add-user-auth --interval-secs 10

Server Mode

smelt serve runs a long-lived daemon that accepts and dispatches jobs. Configure it with a TOML file:

# Queue directory — smelt watches for .toml manifests dropped here
queue_dir = "/tmp/smelt-queue"

# Maximum parallel jobs
max_concurrent = 2

# Retry policy
retry_attempts = 3
retry_backoff_secs = 5

# HTTP API
[server]
host = "127.0.0.1"
port = 8765

# Optional: SSH worker pool for remote dispatch
# [[workers]]
# host = "worker1.example.com"
# user = "smelt"
# key_env = "WORKER_SSH_KEY"
# port = 22

Job Submission

Jobs can be submitted two ways:

  1. Directory watch — drop a .toml manifest file into queue_dir/
  2. HTTP APIPOST /api/v1/jobs with the manifest as the request body

HTTP API Endpoints

Method Path Description
POST /api/v1/jobs Submit a new job (manifest TOML in request body)
GET /api/v1/jobs List all jobs with current status
DELETE /api/v1/jobs/:id Cancel a queued or running job

Authentication

The HTTP API supports opt-in bearer-token authentication via the [auth] section in server.toml. When [auth] is absent, the API is fully open with no authentication required.

Configuration — token values are never stored in config. Instead, you specify the names of environment variables that hold the tokens at runtime:

[auth]
write_token_env = "SMELT_WRITE_TOKEN"   # required — env var holding the read-write token
read_token_env  = "SMELT_READ_TOKEN"    # optional — env var holding the read-only token

Permission model:

  • Read operations (GET, HEAD) — accepted with either the read or write token.
  • Write operations (POST, DELETE, etc.) — require the write token.
  • When read_token_env is omitted, only the write token grants any access.

Error responses:

  • 401 Unauthorized — the Authorization: Bearer <token> header is missing or malformed.
  • 403 Forbidden — the token does not match any configured token, or it matches the read-only token but the request requires write access.

Queue Persistence

Queue state is automatically persisted to queue_dir/.smelt-queue-state.toml after every enqueue, complete, and cancel. On restart, smelt serve reloads this file and re-dispatches any jobs that were queued, retrying, or running at shutdown time — no operator intervention required.

SSH Worker Pools

When [[workers]] entries are present in the server config, smelt serve dispatches jobs to remote hosts via SSH instead of running them locally. Jobs are round-robined across available workers; unreachable workers are skipped and the job is re-queued for the next available host.

Tracker-Driven Dispatch

smelt serve can automatically poll an external issue tracker (GitHub Issues or Linear) for work items tagged with lifecycle labels. When an issue is labelled smelt:ready, the poller picks it up, transitions it to smelt:queued, generates a job manifest from a template, and enqueues the job.

Configuration — add a [tracker] section to server.toml:

# GitHub provider
[tracker]
provider = "github"
repo = "myorg/myrepo"
manifest_template = "path/to/template.toml"
poll_interval_secs = 30
label_prefix = "smelt"
default_harness = "bash"
default_timeout = 600
# Linear provider
[tracker]
provider = "linear"
api_key_env = "LINEAR_API_KEY"
team_id = "team-uuid-here"
manifest_template = "path/to/template.toml"
poll_interval_secs = 60
label_prefix = "smelt"
default_harness = "bash"
default_timeout = 600

The manifest_template must be a valid job manifest TOML with no [[session]] entries — sessions are injected dynamically from tracker issues. The template is validated at startup.

Lifecycle labels — the poller manages label transitions automatically: readyqueuedrunningcomplete / failed. Labels are created in the tracker on first startup if they don't exist.

Tracker-sourced jobs appear in the TUI with source Tracker (vs HTTP for API-submitted jobs or DirWatch for filesystem-watched jobs).

State Backend Passthrough

Job manifests may include an optional [state_backend] section that configures where Assay persists session state inside the container. When present, this section is passed through verbatim into the Assay RunManifest TOML:

[state_backend]
type = "linear"
api_key_env = "LINEAR_API_KEY"
project_id = "project-uuid"
team_id = "team-uuid"

Supported types: "linear", "file" (default when absent). The state backend is opaque to Smelt — it is serialized into the RunManifest and interpreted by Assay at runtime.

Live TUI

By default, smelt serve displays a live Ratatui terminal dashboard showing all jobs, their status, source, assigned worker, and elapsed time. Use --no-tui to disable the TUI and keep tracing output on stderr instead.


Examples

The examples/ directory contains reference manifests:

File Description
job-manifest.toml Standard Docker runtime with multiple sessions
job-manifest-compose.toml Docker Compose runtime for multi-service environments
job-manifest-forge.toml Docker runtime with GitHub forge integration for automatic PR creation
job-manifest-k8s.toml Kubernetes runtime targeting a remote cluster
agent-manifest.toml Minimal agent-focused manifest
bad-manifest.toml Intentionally invalid manifest for testing error handling
server.toml Server daemon configuration for smelt serve (includes auth setup)

Use --dry-run to validate any example without running it:

smelt run examples/job-manifest.toml --dry-run

Ecosystem

Smelt is one layer in a three-part agentic development toolkit:

Layer Project Role
Infrastructure Smelt Container provisioning (Docker, Compose, Kubernetes), environment isolation, forge delivery (PR creation), parallel job dispatch, SSH worker pools
Orchestration Assay Spec-driven sessions, dual-track quality gates, multi-agent coordination inside the container
Context Cupel Token-budgeted context window optimization (library consumed by Assay)

How they connect: smelt run provisions a container and invokes assay run inside it. Assay manages the AI coding sessions using specs from the manifest. Cupel is a library that Assay uses internally to optimize context windows.


Runtimes

Smelt supports three container runtimes, selected via the environment.runtime field in the job manifest:

Runtime Value Use Case
Docker "docker" Single-container jobs (default)
Docker Compose "compose" Multi-service environments (e.g., app + database)
Kubernetes "kubernetes" Remote cluster execution

Project State

Smelt stores per-job run state in .smelt/runs/<job-name>/state.toml. This file tracks the current phase, container ID, sessions, elapsed time, and PR URL (if forge is configured). Use smelt status <job-name> to inspect it.


License

TBD