fabro/lib/crates
fabro-sh-0530[bot] f5f921aa3c
feat: add [run.agent] fabro_tools opt-in for worker run tools (#348)
## Summary

Workflow agents previously always received the `agent:run_tools` JWT
scope and had Fabro run tools registered unconditionally. This PR makes
Fabro run tool access an explicit per-run opt-in via `[run.agent]
fabro_tools = true`, defaulting to `false`.

## What changed

**Config layer** (`fabro-types`, `fabro-config`): `RunAgentSettings`
gains a `fabro_tools: bool` field (serialized with `#[serde(default)]`
for backward compatibility). The corresponding `RunAgentLayer` gets an
optional `fabro_tools: Option<bool>` that resolves to `false` when
absent. Layer merging follows the existing `Combine` macro semantics, so
a workflow-level `false` overrides a user-level `true`.

**Server** (`fabro-server`): `worker_command` gains an
`agent_fabro_tools_enabled: bool` parameter. The server reads
`run_state.spec.settings.run.agent.fabro_tools` from the stored run
before spawning `__run-worker`, then issues the worker JWT with either
`["run:worker"]` or `["run:worker", "agent:run_tools"]` accordingly.
`WorkerScopeSet::run_worker()` loses its `#[cfg(test)]` gate so it's
available in production paths.

**CLI worker** (`fabro-cli`): `FabroRunToolServices` construction is now
gated on `run_spec.settings.run.agent.fabro_tools` rather than being
unconditional. The resolved run spec already carries the setting, so no
env-var parsing is needed in the runner.

**Tests**: The single monolithic
`worker_command_always_sets_worker_token_env` test is replaced by two
focused tests — one confirming the default scope is `["run:worker"]`
only, and one confirming the opt-in scope includes `agent:run_tools`.
Shared assertion logic is extracted into
`assert_worker_command_passes_token_only_by_env` and
`worker_token_claims` helpers. Config resolver tests cover default,
explicit true/false, and layer-override behavior.

**Docs**: `[run.agent]` description and reference tables are updated; a
new `run-configuration.mdx` section explains the opt-in semantics before
the existing `[run.agent.mcps]` section.

### Plan Summary

- 1. Add `fabro_tools` to resolved and layered run config, with resolver
tests.
- 2. Gate worker JWT scope and pass setting from stored run state into
`worker_command`.
- 3. Gate CLI `FabroRunToolServices` construction on the resolved
setting.
- 4. Update docs generator sample and public reference/execution docs.
- 5. Full verification pass (nextest, fmt, clippy).


### Fabro Details

<details>
<summary>Ran 9 stages in 54m 4s for $15.21</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 4s | – | 0 |
| preflight_lint | 2m 18s | – | 0 |
| implement | 28m 5s | $10.58 | 0 |
| simplify_opus | 12m 4s | $2.86 | 0 |
| simplify_gpt | 5m 19s | $1.77 | 0 |
| verify | 3m 37s | – | 0 |
| fmt | 3s | – | 0 |
| **Total** | **54m 4s** | **$15.21** | **0** |

</details>

<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
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."]
    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="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 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, test failures, and generated docs errors.", max_visits=3]
    fmt               [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]

    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 -> fmt   [condition="outcome=succeeded"]
    verify -> fixup
    fixup -> verify
    fmt -> exit
}

```

</details>

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

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: fabro-agent <agent@fabro.local>
2026-05-22 09:41:27 -04:00
..
build-support fix(build): refresh embedded git sha on branch commits 2026-05-09 14:10:04 -04:00
fabro-acp fix(workflow): capture configured artifacts once (#337) 2026-05-21 10:52:22 -04:00
fabro-agent feat(api): bind sessions to runs (#336) 2026-05-21 10:53:41 -04:00
fabro-api Stage-based pairing API and fabro_run_pair MCP tool (#344) 2026-05-21 21:57:55 -04:00
fabro-auth feat(workflow): enforce strict api/acp backends (#307) 2026-05-18 13:20:56 -04:00
fabro-checkpoint Make git metadata sandbox-native 2026-04-27 21:43:15 -07:00
fabro-cli feat: add [run.agent] fabro_tools opt-in for worker run tools (#348) 2026-05-22 09:41:27 -04:00
fabro-client Stage-based pairing API and fabro_run_pair MCP tool (#344) 2026-05-21 21:57:55 -04:00
fabro-config feat: add [run.agent] fabro_tools opt-in for worker run tools (#348) 2026-05-22 09:41:27 -04:00
fabro-core feat: Replace duration/elapsed fields with wall_time_ms and StageTiming (#343) 2026-05-21 21:06:40 -04:00
fabro-dev feat: add [run.agent] fabro_tools opt-in for worker run tools (#348) 2026-05-22 09:41:27 -04:00
fabro-devcontainer refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-dump feat: Replace duration/elapsed fields with wall_time_ms and StageTiming (#343) 2026-05-21 21:06:40 -04:00
fabro-github fix(github): refresh installation tokens during workflows 2026-05-06 07:15:18 -04:00
fabro-graphviz fix(graph): support dotted Fabro graph attributes (#324) 2026-05-20 09:31:08 -04:00
fabro-hooks refactor(hooks): centralize run location handling (#325) 2026-05-20 09:31:46 -04:00
fabro-http refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-install refactor(install): share persistence pipeline (#332) 2026-05-21 08:04:04 -04:00
fabro-interview Stage-based pairing API and fabro_run_pair MCP tool (#344) 2026-05-21 21:57:55 -04:00
fabro-llm refactor(model): move provider facts into catalog (#298) 2026-05-17 20:59:08 -04:00
fabro-macros refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-manifest fix(workflow): allow workflow-root template partials (#322) 2026-05-20 09:22:28 -04:00
fabro-mcp feat(cli): add Fabro MCP server (#236) 2026-05-11 18:20:50 -04:00
fabro-mcp-server feat: improve run board, thread, and MCP create flows (#347) 2026-05-22 09:39:20 -04:00
fabro-model Compute LLM cost on-read for in-flight billing stages (#345) 2026-05-21 19:25:00 -04:00
fabro-oauth fix(error): preserve remaining error context 2026-05-02 10:51:15 -04:00
fabro-options-metadata refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-proc refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-redact refactor(integrations): make chat integrations Slack-only 2026-05-09 11:43:16 -04:00
fabro-sandbox fix(sandbox): derive daytona network policies (#341) 2026-05-21 14:36:04 -04:00
fabro-server feat: add [run.agent] fabro_tools opt-in for worker run tools (#348) 2026-05-22 09:41:27 -04:00
fabro-slack feat(slack): render plan summary + run link in interview messages (re #253, stacked on #252) (#254) 2026-05-13 07:41:54 -04:00
fabro-spa feat(dev): gitignore embedded spa assets 2026-04-26 21:31:11 -04:00
fabro-static refactor(llm): split provider identity from adapters (#280) 2026-05-16 13:13:41 -04:00
fabro-store feat(api): add ask fabro session endpoints (#342) 2026-05-21 21:26:15 -04:00
fabro-telemetry refactor(workflow): remove retro stage (#230) 2026-05-09 10:18:20 -04:00
fabro-template feat(template): resolve template error locations (#333) 2026-05-20 20:15:04 -04:00
fabro-test feat(cli): add Fabro MCP server (#236) 2026-05-11 18:20:50 -04:00
fabro-tool feat: improve run board, thread, and MCP create flows (#347) 2026-05-22 09:39:20 -04:00
fabro-tracker fix(github): refresh installation tokens during workflows 2026-05-06 07:15:18 -04:00
fabro-types feat: add [run.agent] fabro_tools opt-in for worker run tools (#348) 2026-05-22 09:41:27 -04:00
fabro-util refactor(install): share persistence pipeline (#332) 2026-05-21 08:04:04 -04:00
fabro-validate fix(graph): support dotted Fabro graph attributes (#324) 2026-05-20 09:31:08 -04:00
fabro-vault refactor(auth): split credential sources and vault schemas (#306) 2026-05-18 11:07:42 -04:00
fabro-workflow feat: improve run board, thread, and MCP create flows (#347) 2026-05-22 09:39:20 -04:00