Commit graph

70 commits

Author SHA1 Message Date
Bryan Helmkamp
15ff38fa53
feat(template): resolve template error locations (#333)
## Summary

Fixes `fabro-sh/fabro#330` by making template partials that reference
missing inputs validate structurally with a warning instead of failing
the validate command. The template crate now owns MiniJinja semantic
error classification and source-location mapping, so workflow
diagnostics can consume already-resolved template locations instead of
remapping fragment spans itself.

## What Changed

- Added `TemplateErrorLocation` and `TemplateSourceOrigin` APIs to
report source name, line, column, and span from `fabro-template`.
- Classified wrapped MiniJinja errors by their deepest semantic cause,
preserving the original source chain for renderer context.
- Added fragment-origin rendering paths so attribute fragments embedded
in full workflow source report locations in the original source text.
- Removed workflow-side source span remapping from template diagnostics;
workflow now only adds owner, node/edge, severity, rule, and fix
context.
- Added regression coverage for include/import/from/extends undefined
variables and the CLI `fabro validate` partial fixture.

## Test Plan

- `cargo nextest run -p fabro-template`
- `cargo nextest run -p fabro-workflow transforms::variable_expansion
transforms::file_inlining`
- `cargo nextest run -p fabro-cli --test it cmd::validate`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy -p fabro-template -p fabro-workflow
-p fabro-cli --all-targets -- -D warnings`

---

[![Compound
Engineering](https://img.shields.io/badge/Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 (context not reported, reasoning not reported)
via [Codex](https://openai.com/codex)
2026-05-20 20:15:04 -04:00
Bryan Helmkamp
32015b2226
fix(graph): support dotted Fabro graph attributes (#324)
## Summary

Graph rendering now accepts documented Fabro dotted DOT attributes end
to end while keeping raw Graphviz calls behind `fabro-graphviz`. The new
`RenderableDot` boundary applies Fabro render styling and normalization
before raw SVG rendering, and both the CLI subprocess and server path
now route through that typed boundary instead of calling `graphviz_sys`
directly outside the graphviz crate.

The branch also adds a small curated DOT compatibility corpus covering
ACP agent attributes, human default choices, and subworkflow manager
attributes. Those fixtures are exercised by both render and validation
tests, and the run overview now shows graph render errors directly
instead of falling through to the empty graph state.

## Verification

- `cargo nextest run -p fabro-graphviz`
- `cargo nextest run -p fabro-validate`
- `cargo nextest run -p fabro-cli render_graph`
- `cargo nextest run -p fabro-server
render_graph_from_manifest_accepts_fabro_dotted_attributes`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy -p fabro-graphviz -p fabro-cli -p
fabro-server -p fabro-validate --all-targets -- -D warnings`
- `rg -n "graphviz_sys" lib/crates/fabro-cli lib/crates/fabro-server`
- `cd apps/fabro-web && bun test`
- `cd apps/fabro-web && bun run typecheck`

---

[![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: Jess Martin <27258+jessmartin@users.noreply.github.com>
2026-05-20 09:31:08 -04:00
Bryan Helmkamp
21c408d647
fix(workflow): allow workflow-root template partials (#322)
## Summary
- Add a CLI validation regression for workflow-root prompt partials
included from nested prompt files.
- Allow bundled prompt templates to resolve sibling partials from the
workflow root instead of jailing each prompt file to its own directory.
- Refactor include handling so manifest discovery and runtime rendering
share rooted template sources, include normalization, root containment
checks, and FileResolver-backed TemplateStore loading.

## Testing
- `cargo nextest run -p fabro-template`
- `cargo nextest run -p fabro-manifest`
- `cargo nextest run -p fabro-workflow`
- `cargo nextest run -p fabro-cli cmd::validate`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy -p fabro-template -p fabro-manifest
-p fabro-workflow -p fabro-cli --all-targets -- -D warnings`

---------

Co-authored-by: Aleksi Asikainen <1086393+salieri@users.noreply.github.com>
2026-05-20 09:22:28 -04:00
Aleksi Asikainen
492aba7fff
fix: MiniJinja can't find partials (#301)
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
Fixes an issue where use of MiniJinja
[`include`](https://jinja.palletsprojects.com/en/stable/templates/#include)
control structure (`{% include "filename.ext" %}`) causes a render error
`template not found: tried to include non-existing template
"filename.ext"`

### Example broken diagram
``` dot
digraph ValidatePlan {
    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    test_inline_prompt [label="moo" prompt="{% include 'test.tpl.md' %}"]
                                          // ^^^^^^^^^^^^^^^^^^^^^^^^^
    start -> test_inline_prompt -> exit
}
```

### Fix
The core issue was that template rendering knew the source name for
diagnostics, but did not have a loader rooted at the prompt/goal file
location. Includes therefore failed even when the included file existed
next to the rendered file. The fix adds optional loader support to
`fabro-template`, then wires workflow rendering to the existing
`FileResolver` so includes resolve relative to the file currently being
rendered.

For `fabro validate`, there was a second manifest-specific problem:
validation runs through a bundled manifest, and the manifest builder
only bundled explicit `prompt.md` / `goal.md` files, not static
MiniJinja `include` dependencies inside those files. The manifest
builder now scans prompt/goal template text for literal `{% include
"file" %}` / `{% include 'file' %}` references and bundles those files
too. Missing or unsafe include names are left for MiniJinja/runtime
validation rather than expanding scope.

(For clarity: The fix does not support variables or arrays in
`include`.)
2026-05-17 22:15:45 -04:00
Bryan Helmkamp
2b53917759
fix(validate): treat undefined template vars in @file prompts as warnings (#290)
## Summary

`fabro validate` had inconsistent behavior for undefined template
variables depending on whether the prompt was inline or loaded via an
`@file` reference. Inline `{{ inputs.foo }}` produced a warning and
validation passed; the same expression inside a `@file`-imported prompt
produced a hard validation error.

Fixes #286.

## Root cause

Two template-rendering passes with different strictness, applied to
disjoint inputs:

1. **DOT-source pass**
(`lib/crates/fabro-workflow/src/operations/create.rs`) honored
`RenderMode::Structural` for `fabro validate` — undefined variables
downgraded to a `Severity::Warning` diagnostic, then lenient render
finished the job.
2. **Per-attribute pass**
(`lib/crates/fabro-workflow/src/transforms/variable_expansion.rs`)
inside `TemplateTransform` was always strict and had no `RenderMode`
awareness. Because `FileInliningTransform` runs *before*
`TemplateTransform`, expressions inside `@file` content only ever
encountered the strict pass.

## Fix

- Plumb `RenderMode` through `TransformOptions` into
`TemplateTransform`.
- In `RenderMode::Structural`, the transform catches
`TemplateError::UndefinedVariable` per attribute, emits a warning
diagnostic, and falls back to `render_lenient`.
- Diagnostics flow through a new `Transformed.diagnostics` field into
`Validated` alongside lint output.
- Diagnostics now include `node_id` when the undefined variable was
found inside a node attribute, which is more useful than the previous
"at line 1" location.
- `RenderMode` and the shared `template_undefined_variable_diagnostic`
helper moved to `pipeline/types.rs` so the transform layer can reach
them without a circular dep.

Strict mode (`fabro run`, preflight) is unchanged — undefined inputs
still hard-fail before a run is created.

## Behavior

Illustrative output shapes (variable names and line numbers depend on
the fixture):

Inline prompt (unchanged):
```
warning: undefined template variable `inputs.<name>` at line <n> (template_undefined_variable)
Validation: OK
```

`@file`-imported prompt (previously a hard error, now matches inline —
node-attributed instead of line-attributed):
```
warning [node: <id>]: undefined template variable `inputs.<name>` in node `<id>` (template_undefined_variable)
Validation: OK
```

## Test plan

- [x] `cargo nextest run --workspace` — 5773/5773 passing
- [x] `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings` clean
- [x] `cargo +nightly-2026-04-14 fmt --check --all` clean
- [x] New regression test
`bare_fabro_with_unbound_inputs_in_imported_prompt_validates_structurally_with_warning`
in `lib/crates/fabro-cli/tests/it/cmd/validate.rs` against new fixture
`test/templated_unbound_imported/`
- [x] Existing
`bare_fabro_with_unbound_inputs_validates_structurally_with_warning` and
`strict_render_hard_fails_on_unbound_inputs` still pass — verifies
inline structural and run-start strict behavior are both preserved
- [x] Manual reproduction of the exact inputs from the issue now
succeeds with a warning

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

---------

Co-authored-by: Aleksi Asikainen <1086393+salieri@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:31:22 -04:00
Bryan Helmkamp
087c9233f3
fix(validate): pick up sibling workflow.toml inputs for bare .fabro path (#242)
## Summary

- `fabro validate path/to/workflow.fabro` now auto-discovers a sibling
`workflow.toml` and loads its `[run.inputs]`, so templated graphs
validate the same way they do when invoked by name or by toml path.
- The discovery is opt-in to the user's specific graph: we only pick up
the sibling toml if its `[workflow].graph` resolves back to the `.fabro`
the user passed. Unrelated tomls in the same directory are ignored.

## Why

`fabro validate` is the natural fast-feedback tool for CI/pre-commit
hooks that iterate on changed `.fabro` files. Previously, a graph using
`{{ inputs.* }}` would fail with a generic MiniJinja "undefined value"
error when validated by path, even when a sibling `workflow.toml`
defined those inputs. The other two invocation forms (by name, by toml)
worked, which made the path form a usability cliff.

Fixes #195.

## Test plan

- [x] New integration test:
`bare_fabro_picks_up_sibling_workflow_toml_inputs` validates
`test/templated_inputs/workflow.fabro` (uses `{{ inputs.app_dir }}`) and
expects `Validation: OK`.
- [x] New unit tests in `fabro-config::project`:
  - `resolve_workflow_path_picks_up_sibling_workflow_toml` — happy path.
- `resolve_workflow_path_ignores_sibling_toml_pointing_elsewhere` —
guard: don't apply an unrelated sibling toml.
- [x] `cargo nextest run --workspace` — 5585 tests pass.
- [x] `cargo +nightly-2026-04-14 fmt --check --all`, `clippy --workspace
--all-targets -- -D warnings` clean.
- [x] Manual: `fabro validate /tmp/fabro-issue-195/workflow.fabro`
(templated graph + sibling toml with `[run.inputs]`) prints `Validation:
OK`.

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

---------

Co-authored-by: Nate Aune <118984+natea@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:05:21 -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
d92ee47fe3
chore(demo): move simplify prompt to prompts/ alongside graphs
Rename `files-internal/prompts/simplify.md` to `prompts/simplify.md`
adjacent to the .fabro files that reference it, and update the
plan-implement and simplify demos plus the plan-implement test fixture
to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:35:48 -04:00
Bryan Helmkamp
d65c1fa635
refactor(types): remove stage status compatibility 2026-04-30 06:48:47 -04:00
Bryan Helmkamp
9e079b6c0a
feat(test): add DuckDB diff query for bench-tests CSVs
Two queries: per-test p50/p90 regression ordered by largest median delta,
plus a per-package roll-up of total wall-time and quantile shifts. Filters
to passed tests so flakes don't skew medians. Run with `duckdb < test/
analysis/bench-tests-diff.sql` against two CSVs produced by `cargo dev
bench-tests`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 09:27:05 -07:00
Bryan Helmkamp
80de5ca616 refactor(static): centralize env var names
Add fabro-static::EnvVars as the shared registry for fixed environment variable names and migrate env reads, clap env bindings, and subprocess/test allowlists to use it.

Add clippy bans for raw std::env lookup APIs so future dynamic env facades must be documented explicitly.
2026-04-24 12:29:51 -04:00
Bryan Helmkamp
4585f9874c
fix(test): wait for chrome process exit instead of polling for screenshot
The headless Chrome screenshot test polled for the screenshot file every
100 ms for 20 seconds, then panicked. Slow CI runners (Ubuntu 24.04 GHA)
sometimes took longer than the polling deadline, surfacing as a flake.

Chrome with `--screenshot` exits when the file is written, so waiting on
the process is the deterministic completion signal — no polling, no
arbitrary deadline that might be too short.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 17:49:04 -04:00
Bryan Helmkamp
e6e091fe8e
refactor(server): lock down server secrets 2026-04-23 07:15:27 -04:00
Bryan Helmkamp
323c797e0f
refactor(auth): simplify CLI auth plumbing after code review
Consolidate three copies of `normalized_http_base_url` and
`build_public_http_client` into shared helpers in `user_config`,
add `Display for ServerTarget`, drop stale `#[allow(dead_code)]`
markers now that login/logout/JWT are wired, remove dead
`LOGIN_SUCCESSFUL` and `_error_description` field, and gate
test-only helpers behind `#[cfg(test)]`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 11:13:37 -04:00
Bryan Helmkamp
9c68c57bcc
feat(auth): add CLI GitHub login and logout flow
Add the server-side CLI OAuth endpoints and token persistence needed to
mint JWT access tokens and rotating refresh tokens from the existing
GitHub web auth flow.

Add CLI auth storage plus `fabro auth login`, `logout`, and `status`, and
prefer stored OAuth access tokens when building target clients.
2026-04-20 07:12:52 -04:00
Bryan Helmkamp
0b91c71514
test(unwrap): clean shared support and workflow fixtures 2026-04-19 21:01:25 -04:00
Bryan Helmkamp
72924ba611
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-agent/src/cli.rs
#	lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs
#	lib/crates/fabro-cli/tests/it/cmd/exec.rs
#	lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs
#	lib/crates/fabro-telemetry/src/spawn.rs
#	lib/crates/fabro-workflow/tests/it/attractor_compat.rs
#	lib/crates/fabro-workflow/tests/it/cp_integration.rs
2026-04-19 20:32:02 -04:00
Bryan Helmkamp
ad0d532691
chore(clippy): require reasons on allow attributes
Enable clippy::allow_attributes_without_reason at the workspace level.
Add concise, callsite-specific reasons to existing allow attributes, including generated code paths.
2026-04-19 20:24:24 -04:00
Bryan Helmkamp
19939c5f07
lint(clippy): disallow blocking std::fs on Tokio paths
Phase 2/3 of the std::fs lint initiative (Phase 1 refactors landed in
commit 9d1c0d98c).

clippy.toml additions (appended to disallowed-methods):
  std::fs::read, read_to_string, write, read_dir, copy, canonicalize
  std::fs::File::open, File::create, File::create_new
  std::fs::OpenOptions::open

File::options was deliberately excluded — it returns an OpenOptions
builder with no syscall. OpenOptions::open is where the block happens.
Non-blocking std::fs items (metadata, exists, create_dir_all, remove_*,
rename, and all std::fs types) remain legal.

Annotation policy (per updated plan):
  - Mixed async/sync production source: function- or statement-scoped
    #[expect(...)] so future accidental Tokio-path regressions in the
    same file still fire.
  - Fully-sync production source, test modules, integration tests,
    build.rs: file-level #![expect(...)].
  - Every #[expect] has a specific reason identifying the sync context.

Annotations added in ~90 files across the workspace. Notable narrow
placements: fabro-server server.rs current_server_target,
build_disk_usage_response, create_test_app_state_with_session_key;
fabro-server install.rs read_to_string rollback snapshot;
fabro-sandbox local.rs list_recursive; fabro-agent cli.rs FOLLOW-UP on
the JSON-stdout writer; fabro-llm providers/common.rs FOLLOW-UP for
load_file_as_base64 (7 translator call sites; revisit if file:// URL
usage grows).

build.rs blanket allows: fabro-api/build.rs, fabro-util/build.rs.

Pre-existing unrelated nightly-clippy warnings fixed under scope:
fabro-sandbox sandbox_spec.rs (unused_imports, unused_async),
reconnect.rs (unused_variables, unused_async).

Verified: cargo +nightly-2026-04-14 clippy --workspace --all-targets
-- -D warnings passes; fmt clean; 4129/4131 tests pass (two known
flakes under parallel nextest load, both pass individually and are
unrelated to this change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:22:21 -04:00
Bryan Helmkamp
95b101a26f
lint(clippy): disallow blocking std::io and std::net on Tokio paths
Extends the workspace clippy.toml — which already bans std:🧵:sleep,
std:🧵:spawn, and std::process::Command::new on Tokio paths — with:

- disallowed-types: std::io::{Read, Write, BufRead, BufReader, BufWriter}
  and std::net::{TcpStream, TcpListener, UdpSocket}
- disallowed-methods: std::io::{stdin, stdout, stderr}

Non-blocking std::io items (Error, ErrorKind, Result, IsTerminal, Cursor)
remain allowed. std::fs is intentionally deferred.

Annotates ~24 pre-existing sync call sites with #[expect(..., reason = "...")]
matching the established pattern. All annotations describe why blocking I/O
is intentional in that context (sync CLI command, test helper, pre-fork
flush, etc.), so a future conversion to async will surface as an unfulfilled
lint expectation instead of silently drifting.

Fixes one real Tokio-path issue surfaced by the new lint:
fabro-cli's server-start daemon-health poller (try_connect) was a sync fn
called from async execute_daemon; std::net::TcpStream::connect_timeout
blocked a Tokio worker for up to 100ms per poll iteration. Converted to
tokio::net::{TcpStream, UnixStream} with tokio::time::timeout.

One follow-up flagged in-code: fabro-agent/src/cli.rs's JSON event writer
uses std::io::stdout() inside tokio::spawn. Annotated with a FOLLOW-UP
reason pointing at tokio::io::stdout; left unchanged since volume is low
and scope exceeded this pass.

Verified: clippy clean, cargo +nightly fmt --check clean, full nextest
workspace run (4131 passed, 182 skipped).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:06:02 -04:00
Bryan Helmkamp
1048534e2c
ci: switch clippy to pinned nightly, clean up workspace lints
- 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>
2026-04-16 18:59:11 -04:00
Bryan Helmkamp
ce26f66846 feat(release): support prerelease builds
Add prerelease-aware release automation and keep default install and upgrade
paths pinned to the latest stable tag unless an explicit prerelease version is
requested.
2026-04-14 15:43:00 -04:00
Bryan Helmkamp
ba67ad9cea Stop waiting for headless Chrome to exit 2026-04-12 15:12:36 -04:00
Bryan Helmkamp
e828ce35a5 Disable debug UI auto-refresh for screenshot test 2026-04-12 15:06:04 -04:00
Bryan Helmkamp
7771dc1e41 Fix headless Chrome debug UI CI test 2026-04-12 14:54:28 -04:00
Bryan Helmkamp
206cefcadc Merge remote-tracking branch 'origin/main'
# Conflicts:
#	clippy.toml
2026-04-12 13:43:38 -04:00
Bryan Helmkamp
4d925d5d5d refactor(async): lint std::process::Command across all targets
Move async subprocess paths to Tokio or spawn_blocking, document the
intentional synchronous std::process::Command callsites, and make CI run
Clippy with --all-targets so the guardrail applies to test code too.
2026-04-12 13:35:57 -04:00
Bryan Helmkamp
e8ad12fc30 fix: simplify fabro-http crate and fix correctness issues
- Replace unwrap_or_default() with expect() in hooks/llm HTTP client
  builders — Default silently discards all config (timeouts, TLS, proxy)
- Route fabro-mcp through fabro_http instead of raw reqwest, respecting
  FABRO_HTTP_PROXY_POLICY for MCP HTTP transport connections
- Deduplicate HttpClientBuilder / BlockingHttpClientBuilder via macro
- Extract helpers for repeated http_client error handling in diagnostics
  and web_auth
- Remove duplicate test_http_client() in fabro-cli and fabro-llm

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:20:55 -04:00
Bryan Helmkamp
3b2cffceaf refactor(http): centralize reqwest behind fabro-http
Add the shared fabro-http transport crate and route hand-written HTTP client construction through it.

Use FABRO_HTTP_PROXY_POLICY for test no-proxy defaults, remove direct reqwest deps from ordinary crates, and add clippy bans for raw reqwest entrypoints.
2026-04-12 11:48:54 -04:00
Bryan Helmkamp
6a87f0a071 fmt: apply nightly rustfmt after merge
Restore a clean nightly rustfmt baseline on the merged main branch so
cargo +nightly fmt --check --all passes again after bringing in
origin/main.
2026-04-11 13:43:30 -04:00
Bryan Helmkamp
007cfed240 refactor: remove backwards-compat error type aliases
No production deployments exist, so there's no need for migration shims.
Remove all six backwards-compat type aliases (AgentError, SdkError,
CoreError, GraphvizError, StoreError, FabroError) and migrate ~880
callsites to use the canonical Error name directly within each crate,
or qualified imports (e.g., `use fabro_llm::Error as LlmError`) for
cross-crate references. Also fix a pre-existing absolute-path clippy
lint in fabro-server error.rs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:51:42 -04:00
Bryan Helmkamp
5eeacd7864 fmt 2026-04-11 11:27:46 -04:00
Bryan Helmkamp
a1e0762eb0 refactor(workspace): satisfy clippy all-targets warnings 2026-04-05 14:37:32 -04:00
Bryan Helmkamp
8a9d5096e5 test(twin-github): reuse checked-in RSA public key 2026-04-05 12:18:39 -04:00
Bryan Helmkamp
0d9ea168af test: standardize no-proxy localhost HTTP clients 2026-04-05 12:17:29 -04:00
Bryan Helmkamp
093f9c2983 test(twin-github): stop generating rsa keys during tests 2026-04-05 11:02:54 -04:00
Bryan Helmkamp
ddc57d458c Rename SessionConfig to SessionOptions and McpServerConfig to McpServerSettings
Aligns naming with the convention that "Config" is for file-level configuration
while "Options" and "Settings" describe runtime parameters. Also applies
rustfmt formatting fixes in web_auth.rs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 06:47:42 -07:00
Bryan Helmkamp
b56b82d34b Cut over Fabro web app to a server-backed SPA
Replace the old React Router SSR setup with a static SPA build served by
fabro-server, move setup and GitHub auth handling into Rust, and update the
default local web URL and stale Arc-era references to match the Fabro name.
2026-04-01 21:36:01 -07:00
Bryan Helmkamp
c22845e548 Integrate twin-github for fabro-github tests
Add the stripped twin-github test server to the workspace, wire it through
fabro-test, and cover fabro-github's real HTTP auth and pull-request flows
with twin-backed integration tests. This also refactors the GitHub helper
entry points to take explicit base URLs so tests and callers share the same
request path.
2026-04-01 09:48:33 -04:00
Bryan Helmkamp
55181537c3 Suppress unreachable_pub warnings in twin-openai test helpers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 22:41:36 -04:00
Bryan Helmkamp
5b7eabee8f Add twin test mode for OpenAI E2E tests
Integrate twin-openai (fake OpenAI server) into the workspace and wire
it into the e2e_test macro so OpenAI tests can run without real API
credentials. The twin server starts in-process via OnceLock on first use
and provides per-test isolation through bearer-token namespacing.

Changes:
- Add Twin as default TestMode, replacing Off (gating now via #[ignore])
- Extend #[e2e_test] macro with `twin` requirement for twin-only,
  live-only, and dual-mode (twin + live) test gating
- Add e2e_openai!() macro returning (base_url, api_key)
- Convert openai_complete and openai_gpt_5_3_codex_complete to dual-mode
- Add new openai_server_error twin-only test with scripted 500 error
- Standardize axum 0.8 as workspace dependency across all crates
- Relax twin-openai ResponsesRequest to accept unknown fields via flatten

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 20:36:48 -04:00
Bryan Helmkamp
fab59a8298 Extract workflow E2E tests into workflow/ directory
Move the 6 parametrized workflow scenarios from scenario/workflows.rs
into a new workflow/ directory with one file per test. Move fixture
.fabro files from test/scenario/ to workflow/fixtures/ co-located with
the tests.

Rename the scenario_tests! macro to sandbox_tests! in the new module
for clarity. Slim scenario/ down to just lifecycle and exec tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:41:18 -04:00
Bryan Helmkamp
aec2d5b437 Rename --run-dir to --storage-dir, unify with data_dir
Replace the per-run `--run-dir` CLI flag with `--storage-dir` which sets
the base storage directory (default ~/.fabro). Runs are now created under
`<storage-dir>/runs/` automatically. This unifies the server's `data_dir`
config with the CLI by renaming `FabroConfig.data_dir` to `storage_dir`
and adding a `storage_dir()` convenience method.

Key changes:
- FabroConfig: `data_dir` → `storage_dir` (serde alias preserves compat)
- CLI: `--run-dir` → `--storage-dir` on `fabro run`
- `__detached`: now takes `--storage-dir` + `--run-id` instead of `--run-dir`
- All ~20 CLI commands derive runs base from config instead of hardcoded default
- Added parameterized `runs_base(storage_dir)` and `make_run_dir()` helpers
- Updated OpenAPI spec, docs, and all tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:47:07 -04:00
Bryan Helmkamp
588515dbf6 Attractor spec hunks 14 & 16: remove error_policy and k_of_n/quorum from parallel handler
Remove ErrorPolicy enum (continue/fail_fast/ignore) and the k_of_n/quorum
join policies from the parallel handler, leaving only wait_all and
first_success. This deletes ~180 lines of conditional logic including
FailFast early termination, the ParallelEarlyTermination event, and all
related tests and documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:59:35 -04:00
Bryan Helmkamp
b9fe1282d3 Attractor spec hunks 9-11: edge selection fallback and default_max_retries rename
- Remove any-edge fallback from select_edge() in deterministic mode; random
  mode retains it as an enhancement over the base spec
- Restrict preferred_label and suggested_next_ids matching to unconditional
  edges only (already applied in prior work, tests added here)
- Rename default_max_retry → default_max_retries across codebase (code, docs,
  fixtures, skills) and change default from 3 to 0
- Update transitions.mdx to document edge selection cascade accurately

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:20:24 -04:00
Bryan Helmkamp
e5ec057b79 Expand scenario tests to cover more CLI subcommands
Add coverage for validate, model list, workflow list, doctor, exec,
ps, inspect, logs, rm, system df, asset list, asset cp, and cp.
Uses HOME isolation for run lifecycle tests and synthetic assets
for asset/cp testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:05:13 -04:00
Bryan Helmkamp
45bd49bedc Use temp dir for dry-run instead of ~/.fabro/runs to avoid clutter in fabro ps -a
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:27:10 -04:00
Bryan Helmkamp
85db69fdc1 Rename .dot files to .fabro and update all references
Rename 79 workflow files from .dot to .fabro extension across
fabro/workflows/, test/, test/docs/, and files-internal/demo/.
Update TOML configs, Rust production code, test code, and shell
scripts. Backward compat tests in test/attractor/ are unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 22:27:19 -04:00
Bryan Helmkamp
d37fb9a9b4 simplify model stylesheet: rename llm_model/llm_provider, add provider inference
Rename `llm_model` → `model` and `llm_provider` → `provider` in stylesheet
properties, accessor methods, and all DOT/doc references. Add
ProviderInferenceTransform that automatically infers provider from the model
catalog, eliminating redundant provider declarations in stylesheets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:27:18 -04:00
Bryan Helmkamp
376b132ca9 rename docs-internal to files-internal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:50:27 -04:00