Commit graph

284 commits

Author SHA1 Message Date
brynary-fabro[bot]
52a7ba6e9d Unified WorktreeSandbox (#117)
This PR introduces a unified `WorktreeSandbox` type in `fabro-sandbox`
that consolidates previously duplicated git worktree management logic
spread across `parallel.rs` and `run.rs`. The new type wraps any
`Arc<dyn Sandbox>`, handles the full worktree lifecycle (branch
creation, `worktree add`, and cleanup) in its `initialize()`/`cleanup()`
methods, overrides `working_directory()` and `exec_command()` to default
to the worktree path, and delegates all other `Sandbox` methods to the
inner sandbox. A `WorktreeConfig` struct controls behavior (branch name,
base SHA, worktree path, and a `skip_branch_creation` flag for resume
flows), and a `WorktreeEventCallback` mechanism bridges lifecycle events
to the workflow event system via a new
`EventEmitter::worktree_callback()` helper.

The old private `WorktreeSandbox` struct in `parallel.rs` (which only
redirected `exec_command` working dirs with no lifecycle awareness) is
removed and replaced with the shared implementation. The
`setup_worktree()` function in `run.rs` is also removed; its logic is
absorbed directly into the `SandboxProvider::Local` branch of sandbox
construction, where `WorktreeSandbox::initialize()` is called and
`std::env::set_current_dir()` follows on success. The resume path
(`run_from_branch`) similarly replaces direct `git::replace_worktree`
calls with `WorktreeSandbox` using `skip_branch_creation: true`. The
`MockSandbox` in `test_support.rs` gains `captured_commands` and
`captured_working_dirs` vectors to support sequenced-command assertions
in the new unit tests.

The `MockSandbox` enhancement is a notable improvement for testability
beyond this specific change—having the full ordered sequence of commands
rather than just the last one makes it straightforward to assert on
multi-step git workflows. One subtle behavior worth noting is that in
`parallel.rs` the `git reset --hard` step previously present after
worktree creation is now absent from `WorktreeSandbox::initialize()`;
the plan mentioned it but the implementation deliberately omits it (the
branch is already force-set to the target SHA, so the reset was
redundant for the parallel case). Cleanup for parallel branches
continues to go through `engine::git_remove_worktree` on the parent
sandbox rather than calling `wt_sandbox.cleanup()`, since the sandbox
`Arc` is consumed by the spawned task—this is a reasonable tradeoff
noted in the plan.

### Fabro Details

<details>
<summary>Ran 11 stages in 62m 32s for $3.67</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 1m 11s | – | 0 |
| preflight_lint | 12s | – | 0 |
| implement | 30m 15s | $2.15 | 0 |
| simplify_opus | 15m 5s | $0.71 | 0 |
| simplify_gpt | 11m 8s | $0.54 | 0 |
| verify | 46s | – | 0 |
| fixup | 3m 17s | $0.27 | 0 |
| verify | 46s | – | 0 |
| fmt | 1s | – | 0 |
| **Total** | **62m 32s** | **$3.67** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (12 nodes and 15
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 clippy -q --workspace -- -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."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gpt      [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 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 clippy lint warnings and test failures.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 15:35:04 -04:00
Bryan Helmkamp
a7040875cb Merge branch 'docs/update-models-and-cli-reference' 2026-03-19 15:12:39 -04:00
Bryan Helmkamp
d2de2f818c Update docs for provider login, auto-detect provider, and model changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:01:47 -04:00
Bryan Helmkamp
473668fab3 Add changelog entry for 2026-03-19
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:54:56 -04:00
Bryan Helmkamp
8072b271a3 Fix missing skip_clone field in OpenAPI spec and conformance test
The DaytonaConfig struct had a skip_clone field that was missing from
both the OpenAPI spec and the conformance test initializer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:36:49 -04:00
Bryan Helmkamp
3357359474 Escape curly braces in MDX to fix Mintlify deployment
MDX treats {…} as JSX expressions. Bare curly braces in headings
and bold text caused acorn parse failures during Mintlify deploy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:15:16 -04:00
Bryan Helmkamp
7acd2b00dc Document GitHub App visibility requirement for cross-owner installs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:03:20 -04:00
Bryan Helmkamp
8584f59e25 Update changelog and docs for secret management and repo commands
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:08:37 -04:00
Bryan Helmkamp
d5277b9c85 Promote CLI commands to changelog heroes and improve framing
Promote fabro pr, fabro init, fabro diff, fabro preview, fabro graph,
user-level workflows, and GPT-5.4 Mini from accordion items to hero
sections. Reframe lifecycle hooks with positive language.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:01:13 -04:00
Bryan Helmkamp
1d9f491314 Reframe Mar 17 changelog hero around OpenAI OAuth user value
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:59:26 -04:00
Bryan Helmkamp
f5e385c9a1 Update changelog and docs for Mar 16-17 changes
Add March 17 changelog entry (OpenAI Codex backend, fabro docs/discord
commands, gpt-5.4-mini). Update March 16 entry with OAuth error fix.
Add gpt-5.4-mini to model catalog docs and fabro docs/discord to CLI
reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:51:42 -04:00
Bryan Helmkamp
d0b2ff2efc Fix docs footer social links
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 19:03:50 -04:00
Bryan Helmkamp
aa7552c18a Add SVG workflow diagram for REPL Handoff example
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:32:48 -04:00
Bryan Helmkamp
bd315233d1 Remove debug markers from dot-highlight script
Verified script executes and highlights code blocks in production.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:23:48 -04:00
Bryan Helmkamp
d59bbde786 Add debug marker and delay to dot-highlight script
Add data attribute to confirm script execution, and delay
initialization to avoid React hydration clobbering changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:16:00 -04:00
Bryan Helmkamp
cd50af6d95 Add client-side DOT syntax highlighting for docs
Mintlify's custom TextMate grammar support doesn't work in production
builds (see mintlify/discussions#3401). Work around this with a lightweight
JS script that applies regex-based highlighting to code blocks containing
digraph definitions, matching Shiki's inline style format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:07:25 -04:00
Bryan Helmkamp
6717ceb006 Add fabro language for syntax highlighting in docs
Register a separate Shiki grammar so ```fabro code blocks get
DOT syntax highlighting alongside the existing ```dot blocks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:43:14 -04:00
Bryan Helmkamp
9b6650ee48 Remove empty stub doc pages and fix broken links
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:25:05 -04:00
Bryan Helmkamp
459a9c221a Add Codex endpoint support for OpenAI OAuth flow
Route OpenAI OAuth users through the ChatGPT Codex backend API with
required headers (ChatGPT-Account-Id, originator). The Codex endpoint
requires streaming-only requests, omits unsupported fields (temperature,
max_output_tokens, top_p), and uses a different error format. Also
persists the account ID from OAuth tokens and updates CLI docs for
`fabro ps -q`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:47:47 -04:00
Bryan Helmkamp
0d3b6fe73d Update March 16 changelog with auto-merge, lifecycle hooks, and more
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 23:48:03 -04:00
brynary-fabro[bot]
139bf5cbef Move sandbox lifecycle into the engine (#22)
This PR moves sandbox lifecycle management (initialization, setup
commands, devcontainer phases, and cleanup) from the CLI's `run_command`
god function into the workflow engine. Two new engine methods are
introduced: `run_with_lifecycle()` orchestrates sandbox init, fires the
`SandboxReady` hook (now blocking by default), emits a new
`SandboxInitialized` event, handles remote git setup, runs setup
commands and devcontainer lifecycle phases, then delegates to the
existing `run_internal()` graph execution. `cleanup_sandbox()` fires the
`SandboxCleanup` hook and optionally tears down the sandbox. Both
`SandboxReady` and `SandboxCleanup` hook events were previously defined
but never fired — they now fire naturally within the engine alongside
all other hooks.

The CLI is simplified significantly: sandbox record persistence and
progress UI updates are handled via an event listener for
`SandboxInitialized` rather than inline code. The `run_from_branch`
resume path also benefits, gaining hook support and proper cleanup for
free. A new `LifecycleConfig` struct captures setup commands, timeouts,
and devcontainer phases, keeping the engine's API clean. The existing
`run()` method is unchanged, so API server and integration tests
continue working with pre-initialized sandboxes.

The `setup_remote_git` helper is moved from `cli/run.rs` into
`engine.rs` since it only depends on sandbox exec. Config is now passed
by mutable reference to `run_with_lifecycle` so the engine can fill in
remote git fields (base SHA, run branch) that downstream code needs.
Comprehensive tests verify event emission ordering, setup command
execution/failure, and cleanup behavior.

### Fabro Details

<details>
<summary>Ran 10 stages in 53m 27s for $14.42</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $6.22 | 0 |
| simplify_opus | 0s | $2.63 | 0 |
| simplify_gemini | 0s | $2.73 | 0 |
| simplify_gpt | 0s | $2.84 | 0 |
| verify | 0s | – | 0 |
| fmt | 0s | – | 0 |
| **Total** | **53m 27s** | **$14.42** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (13 nodes and 16
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 clippy -q --workspace -- -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."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gemini   [label="Simplify (Gemini)", prompt="@prompts/simplify.md", model="gemini-3.1-pro-preview-customtools"]
    simplify_gpt      [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 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 clippy lint warnings and test failures.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", goal_gate=true, max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gemini -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:00:05 -04:00
brynary-fabro[bot]
61915dee65 Wire up missing hook invocations (#19)
This PR wires up three `HookEvent` variants—`StageRetrying`,
`ParallelStart`, and `ParallelComplete`—that were defined in the enum
and documented but never actually invoked by the engine. `StageRetrying`
hooks now fire at both retry sites in `execute_with_retry` (error-retry
and explicit-Retry-status paths) immediately before the backoff sleep.
`ParallelStart` and `ParallelComplete` hooks fire in the parallel
handler after their corresponding event emissions, using a new
`EngineServices::run_hooks()` convenience method since the handler
doesn't have direct access to the engine's hook method.

The two remaining unwired events, `SandboxReady` and `SandboxCleanup`,
are marked as reserved with doc comments on the enum variants and
annotated in the docs table, since wiring them requires sandbox
lifecycle changes outside the engine's scope. A small
`HookContext::set_node()` helper is introduced to reduce repeated field
assignment across all hook call sites, and existing
`StageStart`/`StageComplete` hook calls are refactored to use it.

### Fabro Details

<details>
<summary>Ran 10 stages in 25m 6s for $5.60</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $1.04 | 0 |
| simplify_opus | 0s | $1.51 | 0 |
| simplify_gemini | 0s | $1.44 | 0 |
| simplify_gpt | 0s | $1.62 | 0 |
| verify | 0s | – | 0 |
| fmt | 0s | – | 0 |
| **Total** | **25m 6s** | **$5.60** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (13 nodes and 16
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 clippy -q --workspace -- -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."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gemini   [label="Simplify (Gemini)", prompt="@prompts/simplify.md", model="gemini-3.1-pro-preview-customtools"]
    simplify_gpt      [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -q --workspace -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 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 clippy lint warnings and test failures.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", goal_gate=true, max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gemini -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
2026-03-16 21:38:53 -04:00
Bryan Helmkamp
359306569a Update roadmap and standardize on Graphviz terminology
Roadmap: replace placeholder items with current shipped/building/planned
features. Use real dates for sorting instead of manual sortOrder. Fix
UTC timezone rendering for date display.

Terminology: replace all standalone "DOT" references with "Graphviz" or
"Graphviz DOT" across docs, marketing, README, AGENTS.md, and OpenAPI
spec. Changelogs left unchanged as historical records.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:01:33 -04:00
Bryan Helmkamp
0587fae2e5 Docs: add auto-merge config, auto-merge to GitHub features, turn-level retries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:06:15 -04:00
Bryan Helmkamp
8586e54596 Add March 16 changelog and update March 15 with new entries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:03:47 -04:00
Bryan Helmkamp
a1497aa19b Fix typos in docs: "appliction" and "an Fabro"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:57:33 -04:00
Bryan Helmkamp
d13972e195 Update install instructions to show Claude Code, Codex, and Bash methods
Match the marketing website's tabbed install widget across README, docs
quick-start, and CLAUDE.md. Add marketing site build/deploy commands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:03:53 -04:00
Bryan Helmkamp
7b452a0783 Add OG image and meta tags for link sharing, remove comma from tagline
- Add 1200x630 branded OG image matching Mintlify docs card style
- Add Open Graph and Twitter Card meta tags to Layout.astro
- Save og-image-template.html for easy regeneration
- Remove comma from "open source, dark software factory" everywhere

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:24:58 -04:00
Bryan Helmkamp
b86851c51a Add REPL Handoff example workflow to docs
Documents the pattern of planning interactively in Claude Code and
delegating implementation to Fabro via the /fabro-implement slash
command, with multi-model simplification and verification gates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:13:05 -04:00
Bryan Helmkamp
8716d235d1 Add auto-merge support for pull requests via GitHub GraphQL API
When `auto_merge = true` is set in `[pull_request]` config, Fabro enables
GitHub's auto-merge on created PRs using the `enablePullRequestAutoMerge`
GraphQL mutation. Auto-merge implies `draft = false` since GitHub doesn't
allow auto-merge on draft PRs. A `merge_strategy` field (squash/merge/rebase,
default squash) controls the merge method. Failures to enable auto-merge
(e.g. repo doesn't have the setting enabled) warn but don't fail the run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 08:37:24 -04:00
brynary-fabro[bot]
bd636a16cd Add --direction flag to fabro graph (#15)
This PR adds a `--direction` (`-d`) flag to the `fabro graph` CLI
command, bringing it to parity with the web UI's LR/TB toggle buttons.
Users can now pass `--direction lr` or `--direction tb` to override the
`rankdir` declared in the DOT source before it's handed off to the `dot`
renderer.

The implementation introduces a `GraphDirection` enum (with `ValueEnum`
and `Display` derives for clap integration), an optional `--direction`
field on `GraphArgs`, and an `apply_direction` helper that uses a
lazy-compiled regex to rewrite `rankdir=…` in the DOT source—mirroring
the same approach used by the web UI. The CLI docs are updated with the
new flag, and two unit tests verify that the rewrite works correctly and
that omitting the flag leaves the source unchanged.

### Fabro Details

<details>
<summary>Ran 10 stages in 17m 6s for $3.60</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $0.68 | 0 |
| simplify_opus | 0s | $1.02 | 0 |
| simplify_gemini | 0s | $0.83 | 0 |
| simplify_gpt | 0s | $1.06 | 0 |
| verify | 0s | – | 0 |
| fmt | 0s | – | 0 |
| **Total** | **17m 6s** | **$3.60** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (13 nodes and 16
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -- -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."]
    simplify_opus     [label="Simplify (Opus)", prompt="@prompts/simplify.md"]
    simplify_gemini   [label="Simplify (Gemini)", prompt="@prompts/simplify.md", model="gemini-3.1-pro-preview-customtools"]
    simplify_gpt      [label="Simplify (GPT-54)", prompt="@prompts/simplify.md", model="gpt-54"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1 && cargo test 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 clippy lint warnings and test failures.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo fmt --all 2>&1", goal_gate=true, max_retries=0]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify_opus -> simplify_gemini -> simplify_gpt -> verify
    verify -> fmt   [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
2026-03-15 23:18:05 -04:00
Bryan Helmkamp
b52026f5e4 Improve hooks docs: multi-layer config, clearer matchers table, sandbox context
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:52:25 -04:00
Bryan Helmkamp
f2bc9610d5 Fix agent SDK docs: correct tool names and AnthropicProfile constructor
- Tool names are shell/read_file/write_file/edit_file/glob/grep/web_fetch/web_search, not Bash/Read/Write/Edit etc.
- AnthropicProfile::new takes only model, not (model, config)
- Add missing web_search to tool list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:09:16 -04:00
Bryan Helmkamp
491ee56de3 Add fabro-agent SDK docs alongside existing fabro-llm reference
Restructure the SDK reference page to cover both crates. The page now
opens with an overview of Fabro's two Rust SDK entry points, followed
by full fabro-agent documentation (Session, SessionConfig, Sandbox,
provider profiles, events, tool hooks, error handling) and the existing
fabro-llm reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:06:07 -04:00
Bryan Helmkamp
30f3c16484 Fix SDK docs: correct failover flags, StreamEvent fields, GenerateResult fields, adapter constructors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:00:53 -04:00
Bryan Helmkamp
63486c1e1d Add Fabro SDK reference page documenting the fabro-llm crate public API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:56:08 -04:00
Bryan Helmkamp
0e0c45d05c Designate retros as experimental, disable by default via [features] flag
Move retro control from [fabro] retro to [features] retros in project
config. Default changes from true to false — retros are now opt-in.
Add retros field to server config Features struct, OpenAPI spec,
TypeScript client, and web app config. Update docs with experimental
warning and new enablement instructions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:36:32 -04:00
Bryan Helmkamp
a54d3876f5 Add cargo-fmt post_tool_use hook and improve matcher docs
Add a blocking cargo-fmt hook to fabro.toml that auto-formats Rust
files after write_file, edit_file, or apply_patch tool calls. Improve
the hooks documentation with a detailed matcher field reference table,
tool name catalog, cross-field matching caveat, and additional examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:34:23 -04:00
Bryan Helmkamp
aee3de78a2 Docs updates: reorder nav, rename DOT Language, simplify dark factory, add fork/upgrade/asset docs
- Move Comparison link below Troubleshooting in nav
- Rename "DOT Language" page to "Fabro Language"
- Remove five-tier table from dark factory page, keep link to Dan Shapiro's post
- Add fork command docs and checkpoints section
- Add upgrade, asset list, asset cp command docs
- Add upgrade_check config reference
- Add retros feature flag to server config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:13:23 -04:00
Bryan Helmkamp
cea079a4b6 Regenerate March 15 changelog with fork, upgrade, and asset commands
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:59:30 -04:00
brynary-fabro[bot]
f9d0ebaf33 Random Edge Selection (#12)
This PR introduces a `selection="random"` node attribute that enables
weighted-random tiebreaking when choosing among candidate outgoing
edges. The existing deterministic behavior (highest weight, then lexical
node ID) remains the default. The cascade priority—conditions →
preferred label → suggested next → unconditional → fallback—is
unchanged; randomness only replaces the final pick-one-from-candidates
step within each tier. A new `weighted_random` function handles the
sampling, treating edges with weight ≤ 0 as weight 1, while a
`pick_edge` dispatcher routes to either the random or deterministic
strategy based on the node's `selection()` accessor.

A validation rule (`RandomSelectionNoConditionsRule`) rejects nodes that
combine `selection="random"` with conditional edges, since condition
evaluation order would conflict with random selection. A companion rule
(`SelectionValidRule`) warns on unrecognized selection values. Both are
registered as built-in lint rules with appropriate error/warning
severities and actionable fix suggestions.

Documentation is updated in the transitions guide with a new "Random
selection" section explaining the behavior and constraints, and the DOT
language reference gains a `selection` row in the node attributes table.
All changes were developed following red/green TDD cycles with
comprehensive test coverage for the accessor, weighted random sampling,
edge selection integration, and both validation rules.

### Fabro Details

<details>
<summary>Ran 7 stages in 17m 1s for $4.07</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $2.43 | 0 |
| simplify | 0s | $1.63 | 0 |
| verify | 0s | – | 0 |
| **Total** | **17m 1s** | **$4.07** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (10 nodes and 13
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -- -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."]
    simplify          [label="Simplify", prompt="@prompts/simplify.md"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1 && cargo test 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 clippy lint warnings and test failures.", max_visits=3]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify -> verify
    verify -> exit  [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
2026-03-15 19:54:48 -04:00
brynary-fabro[bot]
a7faf32e01 Rename [feature_flags] to [features] (#11)
This PR renames the `[feature_flags]` configuration section to
`[features]` and the corresponding `FeatureFlags` type to `Features`
across the entire stack. The change touches the OpenAPI spec (source of
truth), Rust config/API crates, the generated TypeScript API client, the
web application, and the demo config file to ensure consistency.

On the Rust side, the `FeatureFlags` struct becomes `Features` in
`fabro-config`, and all field references (`config.feature_flags` →
`config.features`) are updated in `fabro-api` demo/test code along with
the relevant unit tests. On the TypeScript side, the generated client
reflects the OpenAPI rename (`feature-flags.ts` → `features.ts`,
`FeatureFlags` → `Features`), and manual edits in `fabro-web` update the
`AppConfig` interface, defaults constant (`FEATURES_DEFAULTS`), loader
data destructuring, and template usage throughout `config.server.ts`,
`app-shell.tsx`, and `start.tsx`.

The regeneration also picked up an unrelated new `GitHubConfiguration`
schema that was already present in the OpenAPI spec but hadn't been
generated yet, resulting in the new `git-hub-configuration.ts` file and
its addition to the server configuration type.

### Fabro Details

<details>
<summary>Ran 7 stages in 15m 4s for $3.08</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $2.18 | 0 |
| simplify | 0s | $0.90 | 0 |
| verify | 0s | – | 0 |
| **Total** | **15m 4s** | **$3.08** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (10 nodes and 13
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -- -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."]
    simplify          [label="Simplify", prompt="@prompts/simplify.md"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1 && cargo test 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 clippy lint warnings and test failures.", max_visits=3]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify -> verify
    verify -> exit  [condition="outcome=success"]
    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>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:54:40 -04:00
Bryan Helmkamp
89e4af00a2 Update docs for new CLI commands, GitHub token injection, and events
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:33:18 -04:00
Bryan Helmkamp
7587ab15d4 Add changelog entries for March 14-15
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:28:28 -04:00
arc-1e68f1[bot]
508a865183 Inject GitHub App IAT into Sandbox as GITHUB_TOKEN (#7)
This PR adds GitHub App Installation Access Token (IAT) injection into
sandboxes, allowing `gh` CLI and other GitHub-authenticated tools to
work seamlessly inside workflow sandboxes. Workflow authors can declare
required GitHub permissions in `workflow.toml` under a `[github]`
section (e.g., `permissions = { contents = "write", pull_requests =
"read" }`), with project-wide defaults available in `fabro.toml`.
Workflow-level config fully replaces project-level defaults, consistent
with existing `[pull_request]` behavior.

The implementation introduces a `GitHubConfig` struct wired through
`WorkflowRunConfig`, `RunDefaults`, and `ProjectConfig`, with proper
`apply_defaults` (inherit if unset) and `merge_overlay` (replace if
present) semantics. At runtime, a new `mint_github_token()` helper signs
a JWT, resolves the repo's owner/repo from the origin URL, and requests
a scoped IAT which is injected as `GITHUB_TOKEN` into the sandbox
environment. The previously private
`create_installation_access_token_with_permissions` in `fabro-github` is
made public to support this. A preflight check also mints a token during
validation to surface credential or permission issues early.

Comprehensive tests cover TOML parsing with and without `[github]`,
default inheritance, workflow-over-default precedence, and overlay merge
semantics for `RunDefaults`.

### Fabro Details

<details>
<summary>Ran 7 stages in 27m 15s for $5.88</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $3.79 | 0 |
| simplify | 0s | $2.09 | 0 |
| verify | 0s | – | 0 |
| **Total** | **27m 15s** | **$5.88** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (10 nodes and 13
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    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 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -- -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."]
    simplify          [label="Simplify", prompt="@prompts/simplify.md"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1 && cargo test 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 clippy lint warnings and test failures.", max_visits=3]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify -> verify
    verify -> exit  [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Claude <claude@anthropic.com>
2026-03-15 18:24:30 -04:00
arc-1e68f1[bot]
06b66781d1 Rename metadata branch from refs/fabro/{run_id} to fabro/meta/{run_id} (#3)
* fabro(01KKS6PG9929P116A2RRKXC738): toolchain (success)

Fabro-Run: 01KKS6PG9929P116A2RRKXC738
Fabro-Completed: 2
Fabro-Checkpoint: a274eab045

⚒️ Generated with [Fabro](https://fabro.sh)

* fabro(01KKS6PG9929P116A2RRKXC738): preflight_compile (success)

Fabro-Run: 01KKS6PG9929P116A2RRKXC738
Fabro-Completed: 3
Fabro-Checkpoint: 8ce709ecdb

⚒️ Generated with [Fabro](https://fabro.sh)

* fabro(01KKS6PG9929P116A2RRKXC738): preflight_lint (success)

Fabro-Run: 01KKS6PG9929P116A2RRKXC738
Fabro-Completed: 4
Fabro-Checkpoint: 4f4c237214

⚒️ Generated with [Fabro](https://fabro.sh)

* fabro(01KKS6PG9929P116A2RRKXC738): implement (success)

Fabro-Run: 01KKS6PG9929P116A2RRKXC738
Fabro-Completed: 5
Fabro-Checkpoint: baf4d48a0f

⚒️ Generated with [Fabro](https://fabro.sh)

* fabro(01KKS6PG9929P116A2RRKXC738): simplify (success)

Fabro-Run: 01KKS6PG9929P116A2RRKXC738
Fabro-Completed: 6
Fabro-Checkpoint: ddc0967c0b

⚒️ Generated with [Fabro](https://fabro.sh)

* fabro(01KKS6PG9929P116A2RRKXC738): verify (success)

Fabro-Run: 01KKS6PG9929P116A2RRKXC738
Fabro-Completed: 7
Fabro-Checkpoint: 2249924bb2

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 17:40:51 -04:00
Bryan Helmkamp
24532c2385 Fold CheckpointSaved into CheckpointCompleted
Remove the separate CheckpointSaved event — CheckpointCompleted now fires in
both git and non-git paths. git_commit_sha is Optional (None when git is
disabled or for start nodes). The CheckpointSaved hook event is preserved
unchanged for backward compat with user hook configs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:43:09 -04:00
Bryan Helmkamp
835132cdae Add granular git events, rename GitCheckpoint → CheckpointCompleted
Rename GitCheckpoint/GitCheckpointFailed to CheckpointCompleted/CheckpointFailed
to separate checkpoint lifecycle from git operations. Add 7 new granular git
events: GitCommit, GitPush, GitBranch, GitWorktreeAdd, GitWorktreeRemove,
GitFetch, GitReset. Emit at all relevant call sites in engine.rs and parallel.rs.
Update push helpers to return bool for GitPush success tracking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 14:35:17 -04:00
Bryan Helmkamp
b15d697b23 commas 2026-03-15 12:31:08 -04:00
Bryan Helmkamp
42988044f3 docs 2026-03-15 12:11:54 -04:00