mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
## Summary
Moves environment definitions out of project/workflow TOML config and
into server-owned files, introducing the `fabro-environment` crate and
enforcing source-aware validation so project/workflow/user configs can
no longer define environment catalogs.
### What changed
**New `fabro-environment` crate** — workspace crate wired into
`fabro-cli` and `fabro-server`. Exposes a `seeded_catalog_layer()` that
CLI commands inject at the call site to fill the environment catalog
that settings resolution requires.
**Config environments are now migration-only** — `defaults.toml` no
longer ships a built-in `[environments.*]` catalog. Instead:
- `SettingsSource` enum tags every parsed layer (ActiveSettings,
Project, Workflow, DirectRun, User).
- `validate_settings_source` rejects `[environments.<id>]` in any source
except `ActiveSettings` with a targeted message: `[environments.<id>] is
now server-managed; move this definition to the server environments
directory`.
- TOML-provided
`run.environment.{image,resources,network,lifecycle,labels,volumes,env}`
overrides are also rejected; only `run.environment.id` survives.
**New migration** (`2026052801_settings_environments_to_server_files`) —
chains after the existing legacy-sandbox migration. Extracts
`[environments.*]` entries from `settings.toml` into sibling
`environments/<id>.toml` files, writes a
`.settings-environments-migration.bak` backup, and fails without
modifying any file if a target already exists.
**Builder API additions** —
`RunSettingsBuilder::load_from_with_catalog`,
`load_default_with_catalog`, `from_toml_with_catalog` let callers inject
a server-side catalog; the bare `from_toml` path now errors if no
catalog is present and a named environment is selected.
`WorkflowSettingsBuilder` test helpers in `src/tests/mod.rs` centralise
catalog injection across all config tests.
**`.fabro/project.toml`** — removed the inline
`[environments.fabro-dev]` block (environment definition now lives
server-side).
### Key design decisions
- CLI offline commands (graph, preflight, validate) use
`seeded_catalog_layer()` as a local stand-in until a running server is
available — matches the pre-existing behaviour without regressing
offline workflows.
- `load_settings_path` no longer runs migrations for non-ActiveSettings
sources, preventing project/workflow files from accidentally triggering
file-system writes.
- The `MigrationReport` type is now the new migration's
`SettingsEnvironmentsMigrationReport` (exposes `contents: String`
instead of a parsed layer), keeping `load.rs` simpler and decoupled from
layer parsing.
### Fabro Details
<details>
<summary>Ran 9 stages in 143m 23s for $105.27</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 11s | – | 0 |
| preflight_lint | 2m 23s | – | 0 |
| implement | 28m 27s | – | 0 |
| simplify_opus | 37m 27s | $53.28 | 0 |
| simplify_gpt | 20m 50s | $12.14 | 0 |
| verify | 6m 3s | – | 0 |
| fixup | 45m 15s | $39.84 | 0 |
| **Total** | **143m 23s** | **$105.27** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (11 nodes and 14
edges)</summary>
```dot
digraph ImplementPlan {
graph [
goal="Implement and simplify",
model_stylesheet="
* { model: claude-opus-4-7; }
"
]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0]
preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0]
fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"]
simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"]
verify [label="Verify", shape=parallelogram, script="git fetch origin main 2>&1 && git merge --no-edit --no-stat origin/main 2>&1 && cargo +nightly-2026-04-14 fmt --all 2>&1 && cargo dev docs refresh 2>&1 && cargo +nightly-2026-04-14 fmt --check --all 2>&1 && { command -v rg >/dev/null 2>&1 || { echo 'rg is required for verify'; exit 127; }; } && ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*\"disabled\"' lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml 2>&1 && cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --workspace --status-level slow --profile ci 2>&1 && cargo dev docs check 2>&1 && bun install --frozen-lockfile 2>&1 && (cd apps/fabro-web && bun run typecheck) 2>&1 && (cd apps/fabro-web && bun run test) 2>&1 && (cd lib/packages/fabro-api-client && bun run typecheck) 2>&1 && cargo dev build -- -p fabro-cli --release 2>&1", goal_gate=true, retry_target="fixup"]
fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all format, clippy, Rust test, docs, TypeScript typecheck/test, and build failures.", max_visits=3]
start -> toolchain
toolchain -> preflight_compile [condition="outcome=succeeded"]
toolchain -> exit
preflight_compile -> preflight_lint [condition="outcome=succeeded"]
preflight_compile -> exit
preflight_lint -> implement [condition="outcome=succeeded"]
preflight_lint -> fix_lints
fix_lints -> preflight_lint
implement -> simplify_opus -> simplify_gpt -> verify
verify -> exit [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
156 lines
4.8 KiB
TOML
156 lines
4.8 KiB
TOML
[workspace]
|
|
members = ["lib/crates/*", "test/twin/openai", "test/twin/github"]
|
|
default-members = ["lib/crates/fabro-cli"]
|
|
resolver = "2"
|
|
|
|
[workspace.package]
|
|
edition = "2021"
|
|
version = "0.247.0-nightly.0"
|
|
license = "MIT"
|
|
|
|
[workspace.dependencies]
|
|
agent-client-protocol = { version = "0.11.1", features = ["unstable_session_usage"] }
|
|
agent-client-protocol-tokio = "0.11.1"
|
|
anyhow = "1"
|
|
axum = { version = "0.8" }
|
|
axum-extra = { version = "0.10", features = ["cookie-private", "query"] }
|
|
cookie = { version = "0.18", features = ["percent-encode", "private", "signed", "key-expansion"] }
|
|
croner = "3.0.1"
|
|
thiserror = "2"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = { version = "1", features = ["preserve_order"] }
|
|
tokio = { version = "1", features = ["full"] }
|
|
reqwest = { version = "0.13", default-features = false, features = ["json", "stream", "rustls", "query", "form", "multipart"] }
|
|
sse-stream = "0.2"
|
|
ulid = "1"
|
|
uuid = { version = "1", features = ["v4", "v7", "v8"] }
|
|
rand = "0.9"
|
|
dotenvy = "0.15"
|
|
futures = "0.3"
|
|
tokio-stream = "0.1"
|
|
async-trait = "0.1"
|
|
fs2 = "0.4"
|
|
base64 = "0.22"
|
|
bytes = "1"
|
|
tokio-util = "0.7"
|
|
clap = { version = "4", features = ["derive", "env"] }
|
|
clap_complete = "4"
|
|
jsonschema = { version = "0.42", default-features = false }
|
|
chrono = { version = "0.4", features = ["clock", "serde"] }
|
|
dashmap = "6"
|
|
bollard = "0.18"
|
|
tar = "0.4"
|
|
cli-table = { version = "0.5", default-features = false }
|
|
console = "0.15"
|
|
dialoguer = "0.12"
|
|
git2 = { version = "0.20", default-features = false, features = ["vendored-libgit2", "vendored-openssl", "https"] }
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
|
|
tracing-appender = "0.2"
|
|
rmcp = { version = "1.4", default-features = false }
|
|
walkdir = "2"
|
|
regex = "1"
|
|
semver = "1"
|
|
aho-corasick = "1"
|
|
globset = "0.4"
|
|
dirs = "6"
|
|
mac_address = "1"
|
|
md5 = "0.7"
|
|
mime_guess = "2"
|
|
indicatif = "0.18"
|
|
termimad = "0.34"
|
|
toml = "0.8"
|
|
toml_edit = "0.22"
|
|
jsonwebtoken = { version = "10", features = ["aws_lc_rs"] }
|
|
hkdf = "0.12"
|
|
hmac = "0.12"
|
|
sha2 = "0.10"
|
|
hex = "0.4"
|
|
insta = "1"
|
|
fabro-test = { path = "lib/crates/fabro-test" }
|
|
twin-openai = { path = "test/twin/openai" }
|
|
twin-github = { path = "test/twin/github" }
|
|
tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] }
|
|
futures-util = "0.3"
|
|
daytona-sdk = { git = "https://github.com/brynary/daytona-sdk-rust", rev = "fc58e22f7f25183df6264276ee186bbc32635738", package = "daytona-sdk" }
|
|
daytona-api-client = { git = "https://github.com/brynary/daytona-sdk-rust", rev = "fc58e22f7f25183df6264276ee186bbc32635738", package = "daytona-api-client" }
|
|
sentry = { version = "0.35", default-features = false, features = ["backtrace", "contexts", "ureq", "rustls"] }
|
|
fork = "0.2"
|
|
exec = "0.3"
|
|
slatedb = { version = "0.11.2", features = ["zstd"] }
|
|
object_store = { version = "0.12.5", features = ["aws"] }
|
|
rust-embed = "8"
|
|
percent-encoding = "2"
|
|
minijinja = "=2.19.0"
|
|
miette = { version = "7.6", features = ["fancy"] }
|
|
fabro-http = { path = "lib/crates/fabro-http" }
|
|
fabro-environment = { path = "lib/crates/fabro-environment" }
|
|
fabro-options-metadata = { path = "lib/crates/fabro-options-metadata" }
|
|
fabro-redact = { path = "lib/crates/fabro-redact" }
|
|
fabro-static = { path = "lib/crates/fabro-static" }
|
|
graphviz-sys = { git = "https://github.com/fabro-sh/graphviz-sys" }
|
|
ref-cast = "1"
|
|
strum = { version = "0.28", features = ["derive"] }
|
|
url = "2"
|
|
zeroize = "1"
|
|
|
|
[workspace.lints.rust]
|
|
unsafe_code = "deny"
|
|
unreachable_pub = "warn"
|
|
|
|
[workspace.lints.clippy]
|
|
pedantic = { level = "warn", priority = -2 }
|
|
allow_attributes_without_reason = "warn"
|
|
# Allowed pedantic lints
|
|
implicit_hasher = "allow"
|
|
missing_errors_doc = "allow"
|
|
missing_panics_doc = "allow"
|
|
module_name_repetitions = "allow"
|
|
must_use_candidate = "allow"
|
|
similar_names = "allow"
|
|
struct_excessive_bools = "allow"
|
|
too_many_arguments = "allow"
|
|
too_many_lines = "allow"
|
|
cast_precision_loss = "allow"
|
|
doc_markdown = "allow"
|
|
# Disallowed restriction lints
|
|
print_stdout = "warn"
|
|
print_stderr = "warn"
|
|
dbg_macro = "warn"
|
|
empty_drop = "warn"
|
|
empty_structs_with_brackets = "warn"
|
|
disallowed_methods = "deny"
|
|
exit = "warn"
|
|
get_unwrap = "warn"
|
|
unwrap_used = "deny"
|
|
rc_buffer = "warn"
|
|
rc_mutex = "warn"
|
|
rest_pat_in_fully_bound_structs = "warn"
|
|
use_self = "warn"
|
|
# Project-specific lints
|
|
wildcard_imports = "warn"
|
|
absolute_paths = "warn"
|
|
|
|
[profile.release]
|
|
lto = "thin"
|
|
strip = true
|
|
|
|
[profile.dev]
|
|
debug = "line-tables-only"
|
|
split-debuginfo = "off"
|
|
|
|
[profile.test]
|
|
debug = "line-tables-only"
|
|
split-debuginfo = "off"
|
|
|
|
[profile.dev.package."*"]
|
|
debug = false # Disable debug info for all dependencies
|
|
opt-level = 1 # Shrinks monomorphized generics, reducing test binary size
|
|
|
|
# regex is extremely slow in debug builds (~10s to compile gitleaks patterns)
|
|
[profile.dev.package.regex]
|
|
opt-level = 2
|
|
[profile.dev.package.regex-automata]
|
|
opt-level = 2
|
|
[profile.dev.package.regex-syntax]
|
|
opt-level = 2
|