- rust.yml: move clippy to nightly-2026-04-14 (was stable); also pin
fmt to the same nightly date for consistency. Both jobs now use the
dated nightly and the run-step uses `cargo +nightly-2026-04-14 ...`.
- AGENTS.md: update developer commands to match CI.
- Duration constructors: replace `Duration::from_secs(N * 60)` /
`Duration::from_millis(N * 1000)` with `from_mins` / `from_secs` /
`from_hours` across the workspace to satisfy clippy's new
`duration_suboptimal_units` lint. std::time::Duration only — custom
`settings::duration::Duration` sites kept on `from_secs`.
- map/unwrap_or cleanup: `.map(f).unwrap_or(v)` → `.map_or(v, f)`,
`.map(f).unwrap_or(false)` on Result → `.is_ok_and(f)`, per
`clippy::map_unwrap_or`.
- Misc lints: collapse nested `if` into match guard in
handler/llm/api.rs and run_state.rs; replace `columns.len() > 0`
with `!columns.is_empty()`; switch a pair of `sort_by` calls to
`sort_by_key`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- serve.rs: annotate debug-only `bun run dev` spawn with
#[expect(clippy::disallowed_methods, ...)] and add the missing
watch_web field to three ServeArgs test fixtures.
- install.rs: replace absolute `fabro_server::serve::DEFAULT_TCP_PORT`
path with `serve::DEFAULT_TCP_PORT` (use is already imported) to
satisfy clippy::absolute_paths.
- pagination test: request an explicit page[limit]=100 for the
"fits in one page" case instead of relying on the server default,
so the test stays robust as the built-in model catalog grows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stops any running server then starts a fresh one, passing through all
the same flags as `server start` (--watch-web, --foreground, etc.).
Works even if no server is currently running.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spawns `bun run dev` in apps/fabro-web as a child of the server process,
so a single command starts both the API server and the web asset watcher.
The flag is gated behind #[cfg(debug_assertions)] and does not exist in
release builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
--web-url defaulted to http://localhost:3000 but merge_server_settings
hardcoded http://127.0.0.1:32276, causing GitHub OAuth redirect_uri
mismatch. Now both derive from the same --web-url flag (default:
http://127.0.0.1:32276 via DEFAULT_TCP_PORT).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Print the HTTP URL (cyan) and enabled auth methods after server start,
so users can see at a glance how to access the server and what login
methods are available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update 5 more dry_run_examples snapshots to use [GRAPH_PATH] filter
- Skip LLM preflight check when graph has no LLM nodes (fixes
preflight_allows_pull_request_enabled_without_github_credentials)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add [GRAPH_PATH] filter to run_output_filters so dry_run_simple
snapshot is path-independent
- Double fabro-cli slow-timeout (3s → 6s) to prevent ps test timeouts
- Preserve cloud sandboxes; bump snapshot to fabro-v7 with 8 CPU / 16GB
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The snapshot was overfit to local env — it hardcoded Anthropic models
passing because the server inherited ANTHROPIC_API_KEY from the test
runner. On CI with no API keys, all models are skipped and the snapshot
diverged.
Replace the snapshot with targeted assertions: exit code 0 and "Skipped"
appears in stderr. This works regardless of which credentials are
available in the test environment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Config discovery walks from the workflow file's parent directory, so
tests using fixtures at their repo path (test/simple.fabro) would find
the repo's .fabro/project.toml. This caused settings like preserve=true
to leak into tests and break sandbox cleanup event assertions.
Add TestContext::install_fixture() which copies fixtures into the test's
temp dir. Update all CLI run/attach/start tests to use it. Remove the
now-unused example_fixture() function.
Restore preserve=true in .fabro/project.toml — tests are now isolated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove EnvGuard and env-mutating test from fabro-auth (shared mutable state)
- Revert project.toml preserve=true that broke sandbox cleanup event tests
- Fix clippy: use is_some_and, scoped imports for StageStatus and render
- Update snapshot tests for new Run: ULID line and model_test output
- Fix preflight test assertion (name said "allows", asserted failure)
- Update cancel_queued_run test: cancelled runs now appear on board
- Add graph direction query param support to get_graph endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
StageFailedProps lacked duration_ms, so extract_stage_durations_from_events
only found durations from stage.completed events. Failed stages showed "0s"
in the sidebar despite the command.completed event having the real duration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stages API used checkpoint.current_node to identify the running
stage, but current_node is the last *completed* node — always already
in completed_nodes, so the running-stage check was always false.
Switch to checkpoint.next_node_id which correctly identifies the
currently-executing stage.
Also move SSE subscription from run-detail parent layout into the
StageSidebar component with since_seq=1 to replay all events and
close the race between loader fetch and SSE connection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix run detail status: display actual API status (submitted, running,
succeeded, failed, etc.) instead of always showing "Working"
- Implement /runs/{id}/stages endpoint in non-demo mode, reading from
checkpoint + events to build stage list with statuses and durations
- Fix /runs/{id}/graph to fall through to durable store when run is not
in the live map
- Render real workflow graph SVG on overview and graph pages instead of
hardcoded demo graph; remove unused DotDiagram component from overview
- Add dark mode CSS overrides for server-rendered SVG graphs
- Wire stage detail page to real event data: fetch from /events, filter
by node_id, and render as system/assistant/tool blocks
- Fix stage page 500: use apiJsonOrNull for unimplemented /turns endpoint
- Filter start/exit graph control nodes from stage lists in the UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clarifies that Submitted/Starting runs are initializing, not just
pending. Also refactors run-detail to display the actual run status
via runStatusDisplay instead of mapping to board columns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Daytona SDK client was created via Client::new() which only reads
DAYTONA_API_KEY from process env vars. When the key is stored in the
fabro vault (via `fabro secret set`), it was never forwarded to the SDK,
causing "api_key or jwt_token must be provided" errors.
Thread the API key from the vault through SandboxSpec, DaytonaSandbox,
and reconnect paths so the SDK receives it via new_with_config().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Features like session_sandboxes and retros are server-level capability
flags, not user settings. Expose them on GET /system/info where they
belong alongside other server metadata.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The /boards/runs endpoint was driven by the in-memory state.runs map,
which is empty after server restart. Now reads from SlateDB store so
runs persist across restarts.
Also makes board columns dynamic from the API response instead of
hardcoded in the frontend. Real mode returns: pending, running, waiting,
succeeded, failed. Demo mode returns: working, pending, review, merge.
Board layout changed from fixed 3-column grid to horizontal scroll.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Derive configured providers from env and vault when choosing default
models during run creation and materialization, and thread the resolved
run provider through execution handlers instead of recomputing it.
Also return a user-facing error when fabro-agent cannot infer a default
model for the selected provider.
When `disk_cache = true` in `[server.slatedb]`, Fabro enables SlateDB's
object-store cache at `<storage_root>/cache/slatedb`, caching raw S3
bytes on local disk to reduce read latency. All cache parameters use
SlateDB defaults (16 GB max, 4 MB parts). A warning is emitted if
enabled with `provider = "local"` since the cache adds overhead when
the object store is already on the local filesystem.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Forward route handle to React Router so wide:true works on /runs
- Switch board view to CSS grid for full-width columns
- Remove Verify column, rename Merge to Complete
- Use apiJsonOrNull in workflows/workflow-detail loaders to handle 501
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`fabro secret set` now supports three ways to provide the value: as a
positional arg (existing), piped via --value-stdin, or interactively
when stdin is a TTY (obscured with dialoguer::Password). Diagnostics
remediation messages drop the <value> placeholder to encourage
interactive input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace stringly-typed key construction with a SlateKey builder that
encapsulates the segment separator. Switches from '#' to '\0' so the
separator cannot collide with key segment values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All three login paths (root redirect, dev token, GitHub OAuth callback)
now send users to /runs on first visit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `fabro install github` for reconfiguring GitHub auth on an
existing install without rerunning full setup.
Ensure app/token switches replace stale settings and secrets, and
cover the new flow with CLI and integration tests.
Restart the local server at the end of fabro install so new config and
server.env values take effect immediately. Skip fabro doctor when the
restart fails, and keep targeted unit coverage around the restart and
secret-persistence lifecycle.
Replace the internal positional AppState builder with an AppStateConfig
and route both production and test setup through the new config-backed
path. Preserve the in-process test helper behavior while fixing the
ignored max_concurrent_runs argument with a regression test.
Adds `#[command(alias = "ls")]` to workflow, pr, and artifact list
subcommands for consistency with secret list which already had it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
build_app_state_with_path derived the server.env path from the vault
path's parent directory, causing it to look in vaults/default/ instead
of the storage root. This made fabro doctor report missing GitHub App
credentials even though fabro install saved them correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduces S3 storage cost and read latency for run data by compressing
SST blocks with Zstd. Existing uncompressed data remains readable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The server used option_env!() for FABRO_GIT_SHA and FABRO_BUILD_DATE,
but no build.rs set them — so `fabro version` always showed "unknown".
Add a build.rs to fabro-server (matching fabro-cli's) and remove the
Sandbox line from `fabro system info`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the gh CLI is not installed or not authenticated, the install
wizard now shows "Personal Access Token" (without the gh reference)
and prompts the user to enter their token directly instead of failing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep the render-graph CLI integration test explicitly documented for
synchronous stdio subprocess usage, and make the garbage-stdout server
test drain stdin before returning invalid output so the Linux test path
stays deterministic.
Move the vendored Graphviz FFI crate to its own repo so it can be
reused independently and reduce this repo's footprint (~250 C/H files).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>