Commit graph

4928 commits

Author SHA1 Message Date
Bryan Helmkamp
ba3adb92c7
Serve the local sandbox through the driver Host provider
SandboxSpec::Local and run reconnect now build a DriverSandbox over the
sandbox-driver Host provider instead of fabro's own LocalSandbox, which
is deleted. fabro_sandbox::local_sandbox designates the working
directory (created when missing, never removed), creates the Host handle
in a per-process registry, and learns the platform up front. A local
sandbox reports no provider id: it is its directory, which the run
record already carries, so reconnect rebuilds the handle over that
directory rather than by id.

The credential filter for explicit environment variables and the Bash
readiness probe now come from the exec layer and the driver's activate
helper. Test call sites move to the async constructor; test factories
that must stay synchronous share the parent session's sandbox handle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 16:09:45 -06:00
Bryan Helmkamp
ef69883fdd
Add a fabro Sandbox backed by sandbox-driver facets
DriverSandbox implements fabro's Sandbox trait over a driver handle.
Files go through the Filesystem facet, content and tree search through
the Search facet (grep rendered as path:line:content, walks reported
relative to the caller's base with sizes filled from metadata when the
transport has none), commands through fabro's exec policy, preview URLs
through the access facet, and lifecycle through the handle with fabro's
run events emitted around each step. Initialize and start use the
driver's activate helper, which brings the sandbox to Running and
verifies non-login Bash on both transports, and learn the platform name
from the sandbox itself. Local kinds keep the credential filter on
explicit environment variables; isolated kinds take the caller's
environment as composed. Tests run every operation against the
in-process Host provider.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:50:02 -06:00
Bryan Helmkamp
c9f5ec0a41
Add fabro's exec policy over the sandbox driver Exec facet
fabro_sandbox::exec::SandboxExec runs Bash source through a driver Exec
facet with fabro's own stop policy. Fabro disables the driver's hard
timeout and runs its own timer; a timeout or a caller's cancellation
fires the driver's TERM token, waits the grace period, then fires KILL.
The result reports why fabro stopped the command (timed out or
cancelled) rather than which signal the provider observed, and a stopped
command carries no exit code so a shell's 143 is never read as a program
result. Output streams through the caller's callback, is drained past
the retention cap, and explicit environment variables pass a fail-closed
credential filter on the host and through unchanged on isolated
providers. spawn_stdio wraps the driver's bidirectional process and its
rolling stderr tail in fabro's existing stdio types.

Driver errors now map into fabro's error type as a boxed variant with
accessors for the not-found, unsupported, transport, and incomplete
cases, and the redacted output tail helper reads a driver ExecFailure
as well as fabro's own exec error. The Local sandbox's credential name
filter moves to the exec module so both paths share one policy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:36:25 -06:00
Bryan Helmkamp
4dcbcec400
Add the single sandbox provider construction function
fabro_sandbox::driver::connect_provider turns one
[server.sandbox.providers.<kind>] entry into an Arc<dyn SandboxProvider>
from the sandbox-driver crates. Bundled kinds link the driver's Host,
Docker, and Daytona providers in-process; Daytona receives its API key
and endpoint explicitly through connect_explicit with a
fabro-sandbox/<version> user agent, never from the process environment.
Any other kind launches the configured plugin executable through a
PluginSupervisor and returns a wrapper that replaces a crashed plugin
for new work only, never replaying a failed call. Disabled entries are
refused at the construction point.

The Host and Docker providers also ship as fabro-sandbox-host and
fabro-sandbox-docker plugin executables so CI can drive the bundled
providers over stdio and a deployment can move one out of process by
configuration alone. An integration test registers the Host executable
under the non-bundled kind `host`, creates a sandbox and runs a command
over the wire, then attaches to it by persisted id from a fresh plugin
process, and checks that a plugin declaring a different kind is refused.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:23:56 -06:00
Bryan Helmkamp
80bc51c40e
Open sandbox provider identity to plugin kinds
SandboxProviderKind is now a validated string newtype instead of a
closed enum. The bundled kinds (local, docker, daytona) keep their
constants and a BundledProvider enum for the code paths that still
dispatch on them; any other well-formed sandbox-driver kind name is
accepted and names a plugin executable. EnvironmentProvider is gone:
environment settings carry SandboxProviderKind directly, and
is_clone_based is replaced by a workspace policy where local runs in a
designated directory and every other provider clones.

Server sandbox policy is keyed by kind. [server.sandbox.providers.<kind>]
accepts the bundled kinds with `enabled` and any plugin kind with its
launch settings (path, sha256, dev, args, env, inherit_env); bundled
kinds reject the plugin keys and a kind with no entry is disabled. The
OpenAPI schema, generated Rust and TypeScript clients, web settings
pages, and docs follow. The environments table drops its provider CHECK
enumeration in favour of the kind name rules so a plugin environment
can be stored.

Bundled-only code paths (run start, preflight, reconnect, terminal,
details) now fail with an explicit message for a plugin kind until the
driver construction function lands in the next step.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:17:32 -06:00
Bryan Helmkamp
124065ac52
Fix duration lint in the sandbox driver benchmark
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:08:34 -06:00
Bryan Helmkamp
d88c6064af
Benchmark agent tool calls through the sandbox driver
Phase 2 of the sandbox-driver adoption: an ignored test that unpacks
fabro's own lib tree into each provider and times file reads and
content searches through fabro's current providers, the driver
providers in-process, and the driver providers served over JSON-RPC on
an in-process pipe. Docker reads match, Docker grep is faster through
the driver, Host grep costs about 20 ms more through the derived
search, and the wire hop adds about 0.1 ms per call against the plan's
100 ms per tool call budget. The plan records the full table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 14:30:32 -06:00
Bryan Helmkamp
07c52b07ca
Depend on the sandbox-driver crates by git revision
Fabro pins the sandbox-driver workspace the same way it pins the
Daytona SDK: every crate at one commit on main. The bundled Host,
Docker, and Daytona provider libraries link in-process, and the
protocol crate reaches third-party providers over stdio. Nothing uses
the crates yet; the following commits move fabro onto them one layer
at a time.

The driver pins tracing-subscriber exactly, so the lockfile settles on
that version for the whole workspace.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 14:18:58 -06:00
Bryan Helmkamp
aa8b919f1c
Pin daytona-sdk-rust to the merged main commit
Both fabro and sandbox-driver now pin the same daytona-sdk-rust commit
on main. The newer SDK adds region and sandbox class fields to snapshot
creation; fabro leaves both unset and keeps its current behavior.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 13:45:44 -06:00
fabro-releases[bot]
2f326a13c4 Bump version to 0.348.0-nightly.0
Some checks failed
Rust / Format (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Generated Docs (push) Has been cancelled
Rust / Test (Linux) (push) Has been cancelled
Rust / Test (macOS) (push) Has been cancelled
2026-09-06 09:29:10 +00:00
Bryan Helmkamp
5d290a609b
Merge pull request #844 from fabro-sh/remove/expired-secret-migrations
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
Remove expired startup secret migrations
2026-09-05 14:33:28 -04:00
Bryan Helmkamp
b6482910e5
Remove expired startup secret migrations 2026-09-05 14:05:48 -04:00
Bryan Helmkamp
e5e63c9646
Merge pull request #842 from fabro-sh/fix/replay-conclusion-summaries
Rebuild conclusion stage summaries from run events
2026-09-05 12:05:21 -04:00
Bryan Helmkamp
6cf027282f
Update inspect snapshots for projected conclusion stages 2026-09-05 11:53:44 -04:00
Bryan Helmkamp
a7ed03e175
Rebuild conclusion stage summaries from run events 2026-09-05 11:44:23 -04:00
fabro-releases[bot]
efa0d16588 Bump version to 0.347.0-nightly.0 2026-09-05 09:31:42 +00:00
Scott Werner
5b44e25c7c
Merge pull request #841 from fabro-sh/codex/runnable-startup-reconciliation
Some checks failed
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
Rust / Format (push) Waiting to run
TypeScript / Typecheck (push) Has been cancelled
TypeScript / Test (push) Has been cancelled
TypeScript / Build (push) Has been cancelled
Reconcile runnable runs after server restart
2026-09-04 17:07:23 -04:00
Scott Werner
a712aa4dab Reconcile runnable runs after server restart 2026-09-04 14:15:58 -04:00
Scott Werner
ab7cbeb4ae
Merge pull request #839 from fabro-sh/codex/remove-playground
Remove unsupported workflow playground
2026-09-04 11:18:00 -04:00
Scott Werner
8c0702bfac Remove obsolete playground API route assertion 2026-09-04 10:27:18 -04:00
Scott Werner
7e6bbb243d Remove obsolete playground browser route assertion 2026-09-04 10:13:16 -04:00
Scott Werner
209f45b967
Merge pull request #840 from fabro-sh/cli-missing-default-environment-error
Report available environments when default is missing
2026-09-04 10:09:28 -04:00
fabro-releases[bot]
86101748da Bump version to 0.346.0-nightly.0 2026-09-04 09:35:05 +00:00
Scott Werner
a00b95abe0 Report available environments when default is missing
When `fabro run` or `fabro create` omits `--environment` and the server
has no environment named `default`, the CLI previously failed with only
"could not retrieve environment `default`". It now lists the server's
environment catalog in the error so the user can pass an explicit
`--environment <id>` or create the missing `default` entry. Explicit
`--environment` lookups keep their existing not-found message.

Adds `Client::list_environments` to fabro-client for the catalog read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 12:56:47 -04:00
Scott Werner
f52f2a1edb
Merge pull request #838 from fabro-sh/codex/persist-pre-start-worker-failures
Persist pre-start worker failures
2026-09-03 12:48:39 -04:00
Scott Werner
bdb7b29877
Merge pull request #837 from fabro-sh/codex/reject-local-auto-pr-admission
Reject automatic pull requests for Local runs
2026-09-03 12:40:01 -04:00
Scott Werner
f1b569d9e6 Remove unsupported workflow playground 2026-09-03 12:04:35 -04:00
Scott Werner
c437dc012d Simplify pre-start worker failure handling
Move the "which failures can happen before Starting" classification onto
FailureReason as an exhaustive predicate and use it for every
Runnable -> Failed transition, replacing the hand-maintained allowlist.
Give the pending-cancel precedence rule a single owner shared by the
worker launch and worker exit paths.

Test cleanups: share the Notify wait loop, server record fixture, and
post-failure assertions; simplify the pre-start test runtime's hold
flag; and parameterize the slate run.failed payload helper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 11:45:47 -04:00
Scott Werner
c5010c0b33 Simplify the Local pull-request admission check
Use the shared clone-based provider predicate and rely on the settings
resolver dropping disabled pull-request settings instead of re-checking
the enabled flag. List the new intent-lane error code in the OpenAPI
description, trim the acceptance tests to what they actually prove, and
fold the docs note into the existing requirements sentence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 11:43:29 -04:00
Scott Werner
a2002175e1
Merge pull request #835 from fabro-sh/codex/projected-run-cleanup
Move run projection replay into the summary store
2026-09-03 11:40:45 -04:00
fabro-releases[bot]
a468125ab3 Bump version to 0.345.0-nightly.0 2026-09-03 09:33:32 +00:00
Scott Werner
54666632c8 Persist pre-start worker failures 2026-09-02 17:29:41 -04:00
Scott Werner
cd20e453ec Use a compatible environment in pull request settings test 2026-09-02 17:24:22 -04:00
Scott Werner
18cb752b61 Reject automatic pull requests for Local runs 2026-09-02 17:15:52 -04:00
Scott Werner
0f1e5e1c9c Centralize run projection replay on ProjectedRun
Move ProjectedRun next to EventProjectionCache in run_state, since the
summary store both produces and consumes it, and give it a replay
constructor that owns the events-to-head derivation. load_projection now
returns RunNotFound directly instead of erasing it to None and having
callers rebuild it; load_run_projection is the single Option translation
point. install_in_memory_state reuses the existing From impl, and the
commit path passes its Arc through instead of unwrapping and
reallocating it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 16:33:14 -04:00
Scott Werner
efcf8a0d93
Merge pull request #831 from fabro-sh/codex/cli-run-intent-producer
Create CLI runs from immutable workflow intents
2026-09-02 15:45:21 -04:00
Scott Werner
49fb160108 Use async reads during CLI intent preparation 2026-09-02 13:44:33 -04:00
Scott Werner
2360e8046b Move run projection replay into the summary store 2026-09-02 13:32:43 -04:00
Scott Werner
7c23e08868
Merge pull request #833 from fabro-sh/codex/lazy-run-projections
Load inactive run projections on demand
2026-09-02 13:06:04 -04:00
Scott Werner
e3cbc31ca1 Harden CLI run target identity 2026-09-02 12:59:04 -04:00
Scott Werner
064074233c Narrow pull request recovery to unresolved creation requests
The supervisor's periodic recovery scan asked SQLite for every run that
had ever recorded an explicit pull request creation request and then
replayed each inactive candidate's full history to learn whether the
request was still pending. With projections now loaded on demand that
set grows without bound and was replayed every scan.

The candidate query now mirrors the projection reducer: a run is a
candidate only when its latest creation request has no later request,
created, linked, or unlinked event, and no later failure naming the same
creation id. Callers still replay each candidate to confirm, so the query
only has to avoid omitting a pending run, and the replayed set is bounded
by in-flight requests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 12:48:21 -04:00
Scott Werner
13f3ff867b Simplify on-demand run projection loading
Drop the unreachable active-run mismatch guard that was copied into
load_run_projection: the active-runs map is only ever inserted under the
handle's own run ID, so the check could never fire. Remove it from the two
pre-existing sites too and delete matches_run.

Trim install_in_memory_state to take only the committed projection, since
the event envelope duplicated last_seq and the inner scope only existed to
release the lock before the now-removed shared cache update. Add a From
impl so RunDatabase::build no longer hand-builds EventProjectionCache, and
rename projected_state_locked to match its projection_snapshot sibling.

In fabro-server, have reject_if_archived and ensure_run_exists read the
run summary row instead of replaying the full event history for inactive
runs; the summary is written in the same transaction as the event.

Fold the repeated store-reopen fixtures in fabro-store and fabro-server
tests into helpers, and fix a stale comment about the deleted shared
projection cache.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 11:43:17 -04:00
fabro-releases[bot]
2bf41d908b Bump version to 0.344.0-nightly.0 2026-09-02 09:36:17 +00:00
Scott Werner
ccfd23104d Harden CLI run intent creation 2026-09-01 17:08:34 -04:00
Scott Werner
204dd29e73 Make CLI intent runs independent of run-tool changes 2026-09-01 16:28:58 -04:00
Scott Werner
694d1981ff Simplify the CLI run-intent create path
Quality pass over the intent-producer changes, no behavior changes
intended:

- Move the TOML->JSON scalar conversion into fabro-types as
  toml_scalar_to_json_value, next to its inverse, with typed errors and
  round-trip tests; the CLI now calls the shared helper.
- Reuse goal_layer_from_args for --goal/--goal-file resolution instead
  of a second copy of the exclusivity check and cwd anchoring.
- Delete the dead run_manifest_args helper and the test that kept it
  compiling; preflight_manifest_args is the remaining real builder.
- Make run_target_for_environment a pure (provider, cwd) -> target
  mapping using is_clone_based(), warning at the call site, and default
  the environment id from DEFAULT_ENVIRONMENT_ID instead of a literal.
- Resolve the parent run and retrieve the environment concurrently.
- Drop the ResolvedCommandSettings pass-through struct and the
  duplicated parse-error mapping in the project settings presence read.
- Share the environment/workflow-version/git test mocks from the cmd
  test support module instead of three per-file copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 16:14:34 -04:00
Scott Werner
afe1133878 Fix CLI RunIntent producer CI failures 2026-09-01 16:14:34 -04:00
Scott Werner
2507a0075f Create CLI runs from immutable workflow intents 2026-09-01 16:14:34 -04:00
Scott Werner
c1e9364490 Load inactive run projections on demand 2026-09-01 16:06:54 -04:00
Scott Werner
d260ae89b5
Merge pull request #830 from fabro-sh/codex/sql-run-summary-queries
Move cross-run summaries and pull request recovery to SQLite
2026-09-01 15:19:55 -04:00