Commit graph

114 commits

Author SHA1 Message Date
Bryan Helmkamp
4071908b1d
docs(workflows): document file-based workflow imports
Adds a Defining Workflows page covering the import placeholder
syntax, node ID prefixing, the imported-file contract, default
attribute and class propagation, retry_target remapping, templating
behavior, nested imports, empty-import bypass, and the import_error
validation surface.
2026-05-15 08:02:31 -04:00
Bryan Helmkamp
32f100cbe7
feat(install): make LLM setup optional in web installer and CLI (#265)
Some checks failed
Rust / Clippy (push) Waiting to run
Rust / Format (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
TypeScript / Typecheck (push) Has been cancelled
TypeScript / Test (push) Has been cancelled
TypeScript / Build (push) Has been cancelled
## Summary

Makes LLM setup explicitly skippable in both the web installer and
`fabro install`, without making omission accidental. A skipped LLM step
lets install complete with zero LLM credentials; later LLM-dependent
workflows keep using the existing provider-not-configured behavior.
`fabro doctor` is intentionally unchanged.

Plan: `docs/superpowers/plans/2026-05-14-optional-llm-install.md`

## Key changes

**Server + API**
- `PUT /install/llm` now accepts `{"providers":[]}` as "LLM step
completed, skipped" — the empty-list rejection is removed; per-provider
validation for non-empty lists is retained.
- OpenAPI: dropped `minItems: 1` from
`InstallLlmProvidersInput.providers`, updated schema descriptions so
empty = skipped and `llm: null` = incomplete. TypeScript client
regenerated.
- `/install/finish` still requires the LLM step to be present, but
tolerates zero credentials — it writes settings, runtime auth secrets,
and GitHub secrets normally and writes no LLM vault entries.

**Web installer**
- New "Skip LLM setup" secondary action on the LLM step (via a
`secondaryAction` prop on `StepPanel`) that records an empty provider
list and advances to GitHub.
- Review screen shows `LLM providers: Skipped` (step completed, empty)
vs `Not configured` (step never completed), via a new
`describeLlmSummary` helper.
- Continue with no API keys still shows the existing validation error —
skipping is only reachable through the explicit skip action.

**CLI**
- Interactive `fabro install` asks "Configure LLM providers now?"
(default yes) before provider selection; declining returns an empty
selection and continues to GitHub.
- Hidden non-interactive `--skip-llm` flag, mutually exclusive with
`--llm-provider` / `--llm-api-key-stdin` / `--llm-api-key-env` via clap
`conflicts_with_all`. Missing LLM flags are still validation errors
unless `--skip-llm` is present. Non-interactive usage text updated with
a skip example.

## Code review

Ran a 12-reviewer `ce:review` pass (correctness, testing,
maintainability, project-standards, agent-native, learnings, security,
api-contract, reliability, adversarial, cli-readiness,
kieran-typescript). No P0/P1 findings; agent-native parity PASS. Applied
fixes in `40a29c591`:
- Re-entrancy guard on `runStepSubmit` so a fast double-click on "Skip
LLM setup" can't fire two requests.
- `validate()` only suggests `--skip-llm` in the missing-provider error
when no credential flag is set (it conflicts with those flags).
- Added tests: all three `--skip-llm` conflict arms, the review screen's
"Not configured" branch, and the skip-button failure path.

One advisory finding left as report-only: an empty `PUT /install/llm`
overwrites previously-saved credentials if a user navigates Back and
clicks Skip — judged acceptable since the button is explicitly labeled
and clicking it is deliberate.

## Testing

- `cargo nextest run -p fabro-server -p fabro-cli -p fabro-install` —
1521 passed
- `cargo build -p fabro-api`, `cargo fmt --check`, `cargo clippy`
(changed crates) — clean
- `bun test` (install-app) — 14 passed; `bun run typecheck` — clean
- New coverage: server accepts empty providers + session shows `llm`
complete with `providers:[]`; finish with skipped LLM persists no LLM
vault credentials but keeps GitHub secrets; web skip button PUTs
`providers:[]` and navigates to GitHub; review renders Skipped / Not
configured; CLI `--skip-llm` requires `--non-interactive`, conflicts
with all credential flags, `validate()` succeeds with `--skip-llm`,
usage text documents `--skip-llm`.

Not added (out of plan scope): an automated test for the interactive
`InstallInputSource` skip branch — `InteractiveInstallInputSource` is
TTY-coupled and has no existing tests; the non-interactive `--skip-llm`
path is fully covered.

## Post-Deploy Monitoring & Validation

This change is install-time only; there is no continuous runtime impact.
Validate during the next install/release smoke:

- **Web installer:** run a fresh browser install, click "Skip LLM setup"
on the LLM step, confirm it advances to GitHub and the review screen
reads `LLM providers: Skipped`. Finish the install and confirm the
server restarts into normal mode with no LLM credentials in the vault
(`secrets.json` has no credential entries) and
GitHub/server/object-store/sandbox settings written normally.
- **CLI:** run `fabro install --non-interactive --skip-llm
--github-strategy token --github-username <user>` and confirm it
completes; run interactive `fabro install` and confirm declining
"Configure LLM providers now?" continues to GitHub.
- **Healthy signals:** install completes (web `/install/finish` → 202;
CLI exits 0), server boots in normal mode, `fabro doctor` runs and
reports no LLM providers configured (expected, unchanged behavior).
- **Failure signals / rollback trigger:** install fails to finish,
server fails to boot after a skipped install, or `/install/finish`
rejects a completed-but-empty LLM step. Rollback = revert this PR;
install behavior returns to requiring at least one LLM provider.
- **Validation window/owner:** next install smoke / release
verification, owned by whoever runs the release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 22:34:39 -04:00
Bryan Helmkamp
e7e4fb5ca1
Add Daytona volume mount passthrough (#263)
## Summary

This adds run-configuration support for mounting existing Daytona
volumes into Fabro-managed Daytona sandboxes.

Concretely, this PR:

- adds `[[run.sandbox.daytona.volumes]]` with `volume_id`, `mount_path`,
and optional `subpath`
- resolves that config through the layer/settings/runtime pipeline
- forwards configured mounts to `daytona_sdk::SandboxBaseParams.volumes`
when creating the sandbox
- documents the configuration surface in the Daytona environment and run
configuration docs

## Motivation

Daytona already supports attaching volumes when a sandbox is created,
but Fabro currently owns that sandbox creation call. That means users
cannot attach a pre-created Daytona volume to a Fabro-managed sandbox
from run config.

The intended use is persistent, provider-owned state such as agent
credentials, caches, datasets, or other files that should survive
ephemeral sandbox lifecycles.

## Scope

This is intentionally a narrow passthrough. Fabro does not create,
delete, list, wait on, or otherwise manage Daytona volume lifecycle.
Users create the volume in Daytona first, then reference its `volume_id`
from Fabro run config.

`volumes` defaults to an empty list in resolved settings for backwards
compatibility with existing serialized settings.

## Testing

- `cargo test -p fabro-server
runtime_daytona_config_preserves_volume_mounts`
- `cargo test -p fabro-config resolves_daytona_volume_mounts`
- `cargo test -p fabro-sandbox --features daytona volume_mounts`
- `cargo test -p fabro-workflow
runtime_daytona_config_preserves_volume_mounts`
- `cargo check -p fabro-server`

---

_Re-opened from #262 (originally by @kimprobably) to land a rustfmt fix
— the original PR came from an org-owned fork, which blocks maintainer
pushes. Branch is now on the base repo. Original commit preserved; one
additional commit fixes rustfmt formatting._

Co-authored-by: Tim Keen <tim@keen.digital>
2026-05-14 10:54:43 -04:00
Bryan Helmkamp
c0fe29390a
feat(sandbox): prepare clone layout for multi-repo runs (#250)
## Summary

- Clone primary GitHub repos into provider-owned `/repos/{owner}/{repo}`
paths for Docker and Daytona sandboxes.
- Keep user/agent execution rooted at the workspace symlink, e.g.
`/workspace/{repo}` or `/home/daytona/workspace/{repo}`.
- Persist optional runtime layout metadata (`workspace_root`,
`repos_root`, `primary_repo_path`, `primary_repo_link`) through events,
projections, OpenAPI, Rust API tests, and the TS client.
- Preserve empty workspace behavior and reconnect from stored
`working_directory` for existing run records.

## Verification

- `cargo nextest run -p fabro-sandbox --features docker,daytona`
- `cargo nextest run -p fabro-workflow`
- `cargo nextest run -p fabro-server`
- `cargo build -p fabro-api`
- `cargo nextest run -p fabro-api run_sandbox_json_matches_openapi_shape
sandbox_details_json_matches_openapi_shape`
- `cd lib/packages/fabro-api-client && bun run typecheck`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `git diff --check`

## Notes

- Added ignored live smoke tests for Docker and Daytona layout
validation; they require real provider credentials/runtime.
2026-05-14 09:38:20 -04:00
Bryan Helmkamp
1b6189ee32
docs(llm): finish configurable provider cleanup (#260)
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
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
## Summary

Finish phase 9 of the configurable LLM provider/model work by aligning
public docs, release notes, and guardrails with the implementation
already landed in phases 0-8.

- documents settings-driven providers/models, OpenAI-compatible gateway
examples, typed `extra_headers`, model `api_id`, controls, and per-speed
costs
- adds the 2026-05-13 changelog entry and provider string migration note
- updates the internal phase plan ledger to reflect current
implementation status
- adds a workspace policy test blocking direct production
`Catalog::builtin()` usage outside catalog owner/test code
- clarifies `Provider` as a built-in compatibility enum while open-ended
identity is `ProviderId`

## Verification

- `cargo nextest run -p fabro-dev --features dev --test it policy`
- `cargo dev docs check`
- `cargo nextest run -p fabro-model -p fabro-config -p fabro-auth -p
fabro-llm`
- `cargo build --workspace`
- `cargo nextest run --workspace` (5717 passed, 182 skipped, nextest
reported 1 leaky test)
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `git diff --check`
2026-05-13 17:17:54 -04:00
Bryan Helmkamp
a81eb09e78
feat(llm): add catalog controls and speed billing (#249)
## Summary

This PR advances the catalog-driven LLM work from fabro-sh/fabro#210 by
making the resolved model catalog the source of truth for provider
registration, request control validation, and billing identity. Runs now
preserve canonical provider/model/speed identity through pricing and API
responses instead of collapsing billing around provider API aliases or
model IDs alone.

## What Changed

- Register LLM provider adapters from the resolved catalog, including
custom OpenAI-compatible providers and their credential resolution
paths.
- Validate effective model request controls, including run-level
defaults and node overrides, before dispatching LLM requests.
- Add catalog-aware billing lookup that prices canonical `ModelRef`
values, uses base model costs for standard speed, applies per-speed cost
overrides, and returns an unknown estimate instead of silently billing
zero for unsupported combinations.
- Move Anthropic Opus fast-mode pricing into the built-in catalog for
`claude-opus-4-6` and `claude-opus-4-7`.
- Thread the injected catalog and effective speed controls through
workflow billing, including API-mode and CLI-mode handlers.
- Update billing APIs, server aggregation, generated clients, and the
web billing view to expose provider/model/speed billing identity and
keep standard and fast usage in separate rows.

## Notes for Review

Billing lookup intentionally uses canonical catalog model IDs. Provider
`api_id` substitution remains limited to provider request construction,
so aliases can be used on the wire without changing billing identity.
Event conversion paths that do not have catalog access now preserve
token counts with a null dollar estimate rather than falling back to the
bootstrap catalog.

## Verification

- `cargo build -p fabro-api`
- `cd lib/packages/fabro-api-client && bun run generate`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `ulimit -n 4096 && cargo nextest run -p fabro-model -p fabro-workflow
-p fabro-server -p fabro-api -p fabro-cli --no-fail-fast`
- `ulimit -n 4096 && cargo nextest run --workspace --no-fail-fast`
- `cd apps/fabro-web && bun run typecheck`
- `cd apps/fabro-web && bun test`
- `git diff --check`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:12:16 -04:00
Bryan Helmkamp
6297b200f7
refactor(run): add rich failure contract (#256)
## Summary

Terminal run failures now use a first-class `RunFailure` contract so
downstream consumers receive structured diagnostics instead of flat
`error` / `causes` / `reason` fields. The wire shape keeps concise
public messages, source-chain causes, classification, optional
actor/signature data, and redacted exec output tail in one nested value.

Refs fabro-sh/fabro#198

## What Changed

- Added `fabro_types::RunFailure` and changed `run.failed` to emit
`properties.failure` with `final_git_commit_sha` for failed-run commit
state.
- Replaced `Conclusion.failure_reason` with `Conclusion.failure` while
leaving stage-level `StageCompletion.failure_reason` untouched.
- Updated workflow internals to preserve owned error source chains until
terminal event projection, then convert them into `RunFailure.causes`.
- Updated store, server, CLI, OpenAPI, and generated TypeScript client
consumers to use the nested failure object.
- Added serialization, OpenAPI replacement, projection, and lifecycle
coverage for the new contract.

## Validation

- `cargo nextest run -p fabro-api -p fabro-types -p fabro-workflow -p
fabro-store -p fabro-server -p fabro-cli`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cd apps/fabro-web && bun run typecheck`
- `cd apps/fabro-web && bun test`
- `git diff --check`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
2026-05-13 12:32:28 -04:00
Bryan Helmkamp
34d83db801
feat(model): support open provider catalog data (#245)
## Summary

This PR moves Fabro’s provider/model catalog toward settings-driven
provider identity by replacing the closed provider schema at the
API/auth/model boundary with `ProviderId`, then loading built-in
provider and model metadata from embedded per-provider TOML files.

The immediate result is that built-ins now use the same settings-shaped
catalog data that custom providers will use later, while request-serving
paths still keep the existing bootstrap/default catalog behavior until
the resolved-catalog plumbing lands.

## Changes

- Replaces API-facing provider enum usage with string-backed
`ProviderId`, including OpenAPI/progenitor replacements and regenerated
TypeScript client models.
- Routes model, auth, billing, CLI, server, and workflow call sites
through provider IDs where they cross product identity boundaries.
- Builds `Catalog` from settings-shaped provider/model data with
validation for adapter keys, OpenAI-compatible `base_url`, duplicate
aliases, provider defaults, disabled entries, model controls, and
per-speed cost rows.
- Replaces `catalog.json` with embedded provider TOML files under
`lib/crates/fabro-model/src/catalog/providers/`.
- Adds an explicit `fabro_model::bootstrap_catalog` hatch for
setup/install paths and extends the dev policy test to keep bootstrap
access contained.
- Preserves public training and knowledge-cutoff labels in LLM model
settings while still accepting bare TOML dates.

## Verification

- `cargo nextest run -p fabro-model -p fabro-config -p fabro-api` — 416
passed
- `cargo nextest run -p fabro-dev --features dev
bootstrap_catalog_references_stay_in_allowlist` — 1 passed
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `cargo build --workspace`
- `git diff --check`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
2026-05-12 15:42:49 -04:00
Bryan Helmkamp
a33d17c88d
feat(run): add managed branch controls (#243)
## Summary

Adds run-level controls for clone behavior, managed run branch
setup/pushes, and metadata branch writes/pushes so workflows can opt out
of Fabro-managed Git behavior without relying on provider-specific
`skip_clone` settings. This closes fabro-sh/fabro#240.

## What Changed

- Introduced `[run.clone]`, `[run.run_branch]`, and `[run.meta_branch]`
settings with defaults that preserve current behavior.
- Removed user-facing `skip_clone` from Docker/Daytona config while
mapping the new run-level clone setting into the internal sandbox
runtime options.
- Gated run branch setup/push, metadata branch writer creation/push, and
PR branch output on the new settings.
- Enforced invalid combinations: pull requests require an enabled pushed
run branch, and disabling the run branch also disables metadata branch
behavior.
- Updated OpenAPI, the generated TypeScript API client, frontend fixture
data, and docs for the new configuration shape.

## Testing

- `cargo nextest run -p fabro-config -p fabro-types -p fabro-workflow -p
fabro-server`
- `cargo build -p fabro-api`
- `cd lib/packages/fabro-api-client && bun run generate`
- `cd lib/packages/fabro-api-client && bun run typecheck`
- `cd apps/fabro-web && bun run typecheck`
- `cd apps/fabro-web && bun test`
- `cargo build --workspace`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
- `cargo insta pending-snapshots`
- `git diff --check`

## Post-Deploy Monitoring & Validation

- Validation window: first 24 hours after release; owner: release
owner/on-call engineer.
- Log queries/search terms: `run_branch`, `meta_branch`,
`clone.enabled`, `skip_clone`, `pull request requires an enabled pushed
run branch`, `metadata branch`.
- Healthy signals: runs without custom branch config continue creating
and pushing run/meta branches; runs with `[run.clone] enabled = false`
start provider sandboxes without cloning; runs with branch pushes
disabled complete without Git push errors.
- Failure signals: increased run startup failures for Docker/Daytona,
unexpected PR creation conflicts, missing metadata for default-config
runs, or validation errors for configurations that previously used
default settings.
- Mitigation trigger: if default-config runs stop producing expected
branch/metadata artifacts or sandbox startup failures increase, roll
back the release or temporarily restore previous defaults while
investigating the run-level setting resolution path.

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)

Co-authored-by: Haroldo Olivieri <6575718+haroldolivieri@users.noreply.github.com>
2026-05-12 12:00:30 -04:00
Bryan Helmkamp
5e839ef4ea
docs(mcp): document Fabro MCP server 2026-05-12 08:29:02 -04:00
Bryan Helmkamp
5d227dadc2
docs(changelog): refresh recent product changes 2026-05-12 08:29:02 -04:00
Bryan Helmkamp
234bd5663e
Add ACP backend support (#237)
## Summary
Implemented ACP support as a first-class Fabro backend alongside `api`
and `cli`. This adds a new `fabro-acp` crate using the official ACP Rust
crates, routes `backend=\"acp\"` for agent and prompt nodes, adds
sandbox stdio support for local/Docker/test-support paths, emits ACP
workflow events/projections, updates server steerability handling,
validation, documentation, and black-box CLI coverage.

## Test Plan
Passed strict non-live verification:
- `ulimit -n 4096 && cargo nextest run -p fabro-workflow --run-ignored
all --no-fail-fast` — 1162 passed, 0 skipped.
- `ulimit -n 4096 && cargo nextest run -p fabro-acp -p fabro-sandbox -p
fabro-workflow -p fabro-validate -p fabro-store -p fabro-server -p
fabro-cli --run-ignored all --no-fail-fast -E 'not
test(daytona_streaming_live_smoke)'` — 3125 passed.
- `cargo build --workspace` — passed.
- `ulimit -n 4096 && cargo nextest run --workspace --run-ignored all
--no-fail-fast -E 'not test(daytona_streaming_live_smoke)'` — 5666
passed.
- `cargo +nightly-2026-04-14 fmt --check --all` — passed.
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings` — passed.

Live-environment tests skipped/excluded under explicit user override:
- `daytona_streaming_live_smoke` was excluded from final nextest runs
because it requires live Daytona infrastructure and `DAYTONA_API_KEY`.
- Confirmed with `env -u DAYTONA_API_KEY cargo test -p fabro-sandbox
--features daytona --test daytona_streaming_live
daytona_streaming_live::daytona_streaming_live_smoke -- --ignored
--exact --nocapture`: failed fast with `DAYTONA_API_KEY must be set to
run this live smoke test`.
2026-05-11 23:39:43 -04:00
Bryan Helmkamp
a19f6dd03a
feat(cli): add Fabro MCP server (#236)
## Summary

Adds a stdio-based Fabro MCP server so MCP clients can manage Fabro
workflow runs through the authenticated `fabro` CLI, without a separate
MCP auth flow.

## What Changed

- Adds `fabro mcp start`, `fabro mcp config`, and `fabro mcp init
<agent>` for launching and configuring the MCP server.
- Introduces a new `fabro-mcp-server` crate with run-management tools:
  - `fabro_run_create`
  - `fabro_run_search`
  - `fabro_run_interact`
  - `fabro_run_gather`
  - `fabro_run_events`
- Reuses the CLI's authenticated server connection behavior, including
OAuth refresh, dev-token/local-server handling, explicit server targets,
proxy behavior, and stdio env/cwd isolation.
- Moves shared run-manifest construction into `fabro-manifest` so CLI
runs and MCP-created runs use the same override semantics.
- Extends MCP client stdio support with configured cwd and exact
environment handling for reliable spawned-server tests.

---------

Co-authored-by: fabro-sh-0530[bot] <281434857+fabro-sh-0530[bot]@users.noreply.github.com>
Co-authored-by: Fabro <noreply@fabro.sh>
2026-05-11 18:20:50 -04:00
Bryan Helmkamp
cccb557281
feat(api): unify public run shape
Return canonical Run payloads across run list, board, create, and lifecycle endpoints. Move archive state out of RunStatus and into lifecycle metadata, split sandbox runtime from planned sandbox data, and separate static pull request records from live pull request details.

Regenerate the TypeScript API client and migrate web, CLI, server, store, workflow, and API tests to the new contract.
2026-05-10 20:48:55 -04:00
Bryan Helmkamp
b6d4d240ae
feat(auth): add unified session API
Expose browser and CLI auth sessions through a normalized API, and allow revoking active CLI refresh-token chains while keeping browser sessions non-revocable for v1.
2026-05-10 14:16:23 -04:00
Bryan Helmkamp
5209d05623
refactor(sandbox): unify run sandbox identity
Replace the separate sandbox record shape with a typed RunSandbox model shared by projections, API responses, and generated clients. The public contract now uses SandboxProvider plus a non-null id and working_directory, and removes sandbox identifier/name leakage.
2026-05-10 13:16:57 -04:00
Bryan Helmkamp
47f581cc1d
fix(server): improve sandbox service discovery
Fall back to procfs when ss is unavailable, report the discovery source in API metadata, and surface the sandbox install tip in the services UI. Previewable services are ordered first for clearer service selection.
2026-05-10 12:40:38 -04:00
Bryan Helmkamp
9c08653228
feat(server): list sandbox services 2026-05-10 11:24:39 -04:00
Bryan Helmkamp
ac90bb199c
feat(server): add Daytona VNC preview endpoint
Adds the sandbox VNC API contract, Daytona Computer Use startup flow, signed noVNC preview response, and generated TypeScript client support.
2026-05-10 00:06:43 -04:00
Bryan Helmkamp
36c5a86005
refactor(runs): simplify run projection shape
Make run.created the projection anchor and require canonical run spec/status fields in API and clients.

Collapse diff/checkpoint/conclusion payloads around RunDiff and update server, CLI, workflow, store, and generated clients.
2026-05-09 23:31:43 -04:00
Bryan Helmkamp
02d494c268
feat(sandbox,server,web): add sandbox_details inspection and unify SandboxResources
Adds fabro_sandbox::sandbox_details, a control-plane inspection function
that maps Local, Docker, and Daytona providers into a shared
SandboxDetails record (state, image, resources, labels, timestamps).

To avoid type sprawl, the demo board's SandboxResources is unified with
the new control-plane shape (cpu_cores: f64, memory_bytes: u64,
disk_bytes: u64). The runs board chip in apps/fabro-web converts
memory_bytes back to GB for display.
2026-05-09 21:40:19 -04:00
Bryan Helmkamp
aa7e4dd882
feat(types,api): add SandboxDetails control-plane model and OpenAPI schema
Introduces a provider-neutral SandboxDetails record (state, image,
resources, labels, timestamps) plus a normalized SandboxState enum and
the GET /api/v1/runs/{id}/sandbox operation. The fabro-api crate reuses
the fabro-types definitions through with_replacement, and a new
parity round-trip test asserts type identity and JSON shape.
2026-05-09 21:28:03 -04:00
Bryan Helmkamp
8cdc4a6f28
fix(runs): repair deletion of unreadable runs
Allow forced run deletion to purge durable metadata even when projection replay fails, and add a repair command path for deleting unreadable runs in batch.
2026-05-09 17:45:55 -04:00
Bryan Helmkamp
61ab94195e
feat(sandbox): copy Docker exec access command 2026-05-09 17:05:46 -04:00
Bryan Helmkamp
65c8a12cce
Merge remote-tracking branch 'origin/main' 2026-05-09 15:39:44 -04:00
Bryan Helmkamp
67decad7b5
feat(billing): project live stage token usage
Store live per-stage token counts on StageProjection, carry typed billing model identity through agent.message events, and derive billing rollups from the projection so in-flight stages can report usage before terminal events arrive.
2026-05-09 15:25:51 -04:00
Bryan Helmkamp
010828ae7a
Add run commit diff picker 2026-05-09 14:49:15 -04:00
Bryan Helmkamp
8b7d5bd16c
fix(run-files): simplify scoped diffs to tracked files
Use one git diff command for working-tree scopes and exclude untracked files from all scoped run-file views. Update the API description to document the tracked-file scope semantics.
2026-05-09 13:38:44 -04:00
Bryan Helmkamp
447b94da6d
feat(run-files): add sandbox diff scopes
Add committed, uncommitted, and all scope handling for run files with source reporting for sandbox and final patch responses.

Wire the run files page to persist scope in the URL and cache each scope independently.
2026-05-09 13:00:14 -04:00
Bryan Helmkamp
48545f4a7d
Merge remote-tracking branch 'origin/main' 2026-05-09 11:43:59 -04:00
Bryan Helmkamp
8ad14faa17
refactor(integrations): make chat integrations Slack-only 2026-05-09 11:43:16 -04:00
Bryan Helmkamp
697dc1294f
feat(runs): support explicit run titles
Persist resolved run titles on creation, expose title update events, and add the run title PATCH API. Regenerate API clients and refresh web/server invalidation so title changes are reflected across run detail and board views.
2026-05-09 11:18:12 -04:00
Bryan Helmkamp
1ff30ea03c
Merge remote-tracking branch 'origin/main' 2026-05-09 11:05:15 -04:00
Bryan Helmkamp
2a8884883a
refactor(workflow): remove local worktree mode
Make local sandbox execution direct by removing the public worktree mode and in-place controls from CLI, config, run state, API surfaces, docs, and UI. Keep worktree support only for internal parallel-node isolation.
2026-05-09 11:04:23 -04:00
Bryan Helmkamp
5b0b1efdc2
fix(workflow): ignore deprecated project directory
Project workflows now resolve from the discovered .fabro directory instead of honoring project.directory. Keep the legacy field parse-only while removing it from resolved settings and API/client shapes.
2026-05-09 10:55:56 -04:00
Bryan Helmkamp
5fc9157017
refactor(workflow): remove retro stage (#230)
## Summary

Removes Fabro's automatic retro generation stage so workflow runs go
directly from execution to finalization and optional PR creation. This
drops the retro-specific crate, events, projection fields, config/API
knobs, and user-facing docs in favor of the existing durable run
observability surfaces.

## What Changed

- Deleted the `fabro-retro` crate and the workflow `retro` pipeline
phase, with finalization now consuming `Executed` state directly.
- Removed retro configuration and API surface area, including
`--no-retro`, `[run.execution].retros`, manifest `no_retro`,
`features.retros`, and run projection `retro*` fields.
- Retired typed `retro.*` events while keeping historical event logs
readable by deserializing retired retro event names as `Unknown`.
- Stopped appending retro sections to generated PR bodies and updated
docs, marketing copy, screenshots, and navigation to point users toward
observability/event-stream inspection.

## Testing

Not run during PR creation; this branch already contained the
implementation commit.

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 (unknown context, reasoning unspecified) via
[Codex](https://openai.com/codex)
2026-05-09 10:18:20 -04:00
Bryan Helmkamp
f07bb4aaba
feat(cli): support sparse input overrides (#222)
## Summary
- Add repeatable `-I` / `--input KEY=VALUE` CLI overrides for workflow
run inputs on `fabro run`, `fabro create`, and `fabro preflight`. CLI
inputs are sparse per-key overrides that merge over the resolved config
inputs (preserving unrelated inherited values), unlike TOML
`[run.inputs]` which still replaces wholesale.
- Manifest bundling and graph-level goal resolution render workflow
source with the effective inputs before structural scanning, so
input-driven `@prompt`, `import`, and `stack.child_workflow` paths get
bundled correctly.
- Persist raw `KEY=VALUE` strings on `ManifestArgs.input` so server-side
replay applies the same sparse overrides on top of merged config.
- Review-driven cleanups: shared `TemplateContext::for_input_scan`
helper for the recurring "render inputs but defer goal" idiom (replaces
4 sites), `#[derive(Default)]` on `ManifestBuildInput` to drop
boilerplate, inline trivial `apply_input_overrides` wrapper, drop a
redundant clone, and tighten the parser/test helpers.

## Test plan
- [ ] `cargo nextest run -p fabro-cli -p fabro-config -p fabro-server -p
fabro-template -p fabro-workflow`
- [ ] `cargo +nightly-2026-04-14 fmt --check --all`
- [ ] `cargo +nightly-2026-04-14 clippy -p fabro-cli -p fabro-config -p
fabro-server -p fabro-template -p fabro-workflow --all-targets -- -D
warnings`
- [ ] Smoke: `fabro run <workflow> -I key=value --input other=42`
overrides those keys while preserving unrelated inherited inputs
- [ ] Smoke: `-I` accepts strings, integers, floats, booleans, empty
values; rejects arrays, inline tables, datetimes; rejects missing `=`
and empty key
- [ ] Smoke: input-driven `@prompts/{{ inputs.foo }}` and
`stack.child_workflow="{{ inputs.bar }}/workflow.fabro"` paths bundle
correctly when overridden via `-I`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 10:00:35 -04:00
Bryan Helmkamp
d3e33ce32c
feat(runs): own sandbox lifecycle
Create run-owned sandbox lifecycle operations so terminal runs stop by default, resumes attach and start persisted sandboxes, and run deletion deletes or hands off provider resources according to preserve settings.
2026-05-08 18:38:08 -07:00
Bryan Helmkamp
3abf698273
docs: sync command output docs 2026-05-08 17:16:17 -07:00
Bryan Helmkamp
f2a37a329e
docs(changelog): refresh recent product changes 2026-05-08 14:44:18 -07:00
Bryan Helmkamp
1a43cf5abc
feat(api): expose stage handlers on run stages
Populate RunStage.handler from workflow graph metadata and use it for the run stages renderer instead of inferring from activity events.
2026-05-08 13:36:02 -07:00
Bryan Helmkamp
0b29dbe6d8
feat(runs): link stored pull requests 2026-05-08 13:17:08 -07:00
Bryan Helmkamp
b0dfd6b3b4
fix(api): type interview answer submissions
Replace the loose interview answer request payload with a discriminated OpenAPI union so generated clients enforce the wire contract. Surface structured HTTP error details in the web client and update browser, CLI, and server answer submission paths to use the typed variants.
2026-05-08 09:18:35 -07:00
Bryan Helmkamp
9b9ebdf50b
feat(web): migrate to generated API client
Expand the OpenAPI contract for frontend auth and workflow routes, regenerate the TypeScript Axios client, and route web API calls through generated client classes while preserving SSE and install exceptions.
2026-05-08 07:44:33 -07:00
Bryan Helmkamp
d84e7a28ef
feat(workflows): add interview workflow
Add a progressive human interview workflow and teach human gates to honor explicit question_type values so the workflow can exercise yes/no, confirmation, multiple-choice, multi-select, and freeform prompts before summarizing the answers.
2026-05-08 07:44:33 -07:00
Bryan Helmkamp
befb2e00ec
feat(runs): merge command output streams
Route command stderr into stdout at execution time and expose a single output log across events, projections, API clients, and the web UI. Keep replay compatibility for older command.completed events that still contain split stdout/stderr fields.
2026-05-07 22:07:13 -07:00
Bryan Helmkamp
23cb211cce
feat(runs): surface diff summary counts
Compute cheap diff stats on checkpoint and terminal events, roll them into run summaries, and use them for the Files Changed tab badge without fetching full file diffs.
2026-05-07 17:34:32 -07:00
Bryan Helmkamp
ac0492a617
feat: track last_event_at on runs and show it in the run header
Add a `last_event_at` timestamp to RunProjection (set in apply_event so
every event ticks the field) and surface it through RunSummary and the
RunListItem board response. Backed by an OpenAPI extension so both the
Rust and TypeScript clients pick up the new optional field.

In the web UI, the run-detail header gains a "Last activity Xm ago"
badge next to the elapsed-time chip, driven by a 30-second ticker so the
relative time stays current between event refreshes.

The fabro-server tests.rs hunk is incidental rustfmt drift surfaced by
running `cargo fmt --all` over the workspace; including it keeps CI's
fmt-check green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:15:04 -07:00
Bryan Helmkamp
c8336a250e
feat: show archived runs on the runs page
Extend GET /api/v1/boards/runs with include_archived=true (matching the
existing flag on listRuns), add an Archived BoardColumn that the server
appends only when the flag is set, and surface a "Show archived" toggle
on /runs that flips between request shapes. Default behavior is unchanged
— archived runs stay hidden.

Server: list_board_runs now takes ListRunsParams; board_column maps
RunStatus::Archived to BoardColumn::Archived; board_columns(include_archived)
appends the column conditionally. Two new handler tests cover the default
and flag-on paths.

Web: useBoardsRuns(includeArchived) keys requests so SWR refetches on
toggle; columnStatuses + columnStatusDisplay + columnStyles get an
"archived" entry; buildSkeletonColumns filters by the flag so the loading
state matches the eventual response. Two new buildBoardColumns tests cover
both column shapes.

Touched generated TS client files include unrelated whitespace drift from
openapi-generator-cli; including them keeps the working tree consistent
with what `bun run generate` produces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:36:04 -07:00
Bryan Helmkamp
3bfb012fab
feat(cli): split run events and raw logs
Make fabro events the event-stream command and repurpose fabro logs for the per-run worker tracing log returned by the server.
2026-05-06 13:29:35 -04:00