fabro/lib/crates/fabro-api/tests
fabro-sh-0530[bot] 94c657b92e
feat: add run.checkpoint.skip_git_hooks to bypass Git commit hooks (#355)
## Summary

Adds an opt-in `skip_git_hooks` boolean to `[run.checkpoint]` that
causes Fabro-managed run-branch checkpoint commits to pass `--no-verify`
to `git commit`, bypassing local hooks such as `pre-commit` and
`commit-msg`. Defaults to `false`. Metadata-branch snapshots and Fabro
`[[run.hooks]]` are unaffected.

```toml
[run.checkpoint]
skip_git_hooks = true
```

### Plan Summary

- `RunCheckpointSettings` (dense, in `fabro-types`) gains
`skip_git_hooks: bool` with `#[serde(default)]`.
- `RunCheckpointLayer` (sparse, in `fabro-config`) gains
`skip_git_hooks: Option<bool>` so layered config can distinguish unset
from explicit `false`.
- `RunCheckpointLayer::combine` is refactored from a wholesale-replace
to field-level merging: `exclude_globs` keeps its existing replace-wins
semantics; `skip_git_hooks` uses `.or()` (highest-priority layer that
sets it wins).
- `resolve_checkpoint` resolves `None → false`.
- `git_checkpoint` / `checked_git_checkpoint` in `sandbox_git.rs` accept
a new `skip_git_hooks: bool` and append `--no-verify` when true.
- `parallel_branch_commit_cmd` (new helper in `handler/parallel.rs`)
replaces the inline format string and accepts the same flag.
- `GitState` carries `checkpoint_skip_git_hooks`;
`RunOptions::checkpoint_skip_git_hooks()` exposes it; `execute.rs` and
`git.rs` thread it through.
- OpenAPI schema, TypeScript API client, and docs are updated.

### Key design decisions

**Field-level merging in `combine`**: the previous
`RunCheckpointLayer::combine` replaced the whole struct when
`self.exclude_globs` was non-empty. The refactor keeps that same replace
rule for `exclude_globs` while adding independent `Option::or` merging
for `skip_git_hooks`, so the two fields don't interfere.

**`--no-verify` only on run-branch commits**: the flag is injected only
in the two Git commit paths Fabro controls for run-branch checkpoints.
Metadata-branch snapshots use `git2` and never fire local hooks
regardless of this setting.


### Fabro Details

<details>
<summary>Ran 9 stages in 48m 3s for $13.91</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 3s | – | 0 |
| preflight_compile | 2m 4s | – | 0 |
| preflight_lint | 2m 15s | – | 0 |
| implement | 24m 13s | $10.38 | 0 |
| simplify_opus | 10m 31s | $1.59 | 0 |
| simplify_gpt | 5m 8s | $1.93 | 0 |
| verify | 3m 4s | – | 0 |
| fmt | 3s | – | 0 |
| **Total** | **48m 3s** | **$13.91** | **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>
2026-05-22 17:26:06 -04:00
..
billed_token_counts_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
command_termination_round_trip.rs feat(command): distinguish cancelled commands from timeouts 2026-04-30 22:45:37 -04:00
diff_stats_round_trip.rs refactor: unify duplicate API types via with_replacement 2026-04-28 12:29:37 -07:00
diff_summary_round_trip.rs feat(runs): surface diff summary counts 2026-05-07 17:34:32 -07:00
event_envelope_round_trip.rs refactor(api): reuse canonical run events 2026-04-29 20:36:59 -04:00
git_context_round_trip.rs refactor: unify duplicate API types via with_replacement 2026-04-28 12:29:37 -07:00
interview_option_round_trip.rs refactor(api): align run projection types 2026-04-29 20:32:50 -04:00
interview_question_record_round_trip.rs refactor(api): align run projection types 2026-04-29 20:32:50 -04:00
model_costs_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
model_features_round_trip.rs refactor(llm): split provider identity from adapters (#280) 2026-05-16 13:13:41 -04:00
model_limits_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
model_round_trip.rs refactor(llm): split provider identity from adapters (#280) 2026-05-16 13:13:41 -04:00
model_test_mode_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
pair_round_trip.rs Stage-based pairing API and fabro_run_pair MCP tool (#344) 2026-05-21 21:57:55 -04:00
pending_interview_record_round_trip.rs refactor(runs): simplify run projection shape 2026-05-09 23:31:43 -04:00
pre_run_push_outcome_round_trip.rs refactor: unify duplicate API types via with_replacement 2026-04-28 12:29:37 -07:00
principal_round_trip.rs feat(web): show creator avatar on run "Created by" cell (#319) 2026-05-20 09:11:57 -04:00
provider_id_round_trip.rs feat(server): add GET /api/v1/providers and /settings/models page (#321) 2026-05-20 09:05:00 -04:00
provider_round_trip.rs feat(server): add GET /api/v1/providers and /settings/models page (#321) 2026-05-20 09:05:00 -04:00
pull_request_round_trip.rs feat(pr): support GitHub pull request associations (#270) 2026-05-16 12:47:27 -04:00
question_type_round_trip.rs refactor(api): unify leaf API types 2026-04-29 20:21:23 -04:00
run_billing_stage_round_trip.rs feat: Replace duration/elapsed fields with wall_time_ms and StageTiming (#343) 2026-05-21 21:06:40 -04:00
run_event_round_trip.rs Stage-based pairing API and fabro_run_pair MCP tool (#344) 2026-05-21 21:57:55 -04:00
run_failure_round_trip.rs feat: Replace duration/elapsed fields with wall_time_ms and StageTiming (#343) 2026-05-21 21:06:40 -04:00
run_integrations_round_trip.rs Move GitHub token permissions to [run.integrations.github.permissions] (#215) 2026-05-05 15:33:31 -04:00
run_projection_round_trip.rs feat(api): unify public run shape 2026-05-10 20:48:55 -04:00
run_sandbox_round_trip.rs feat(sandbox): prepare clone layout for multi-repo runs (#250) 2026-05-14 09:38:20 -04:00
run_summary_round_trip.rs feat(api): add ask fabro session endpoints (#342) 2026-05-21 21:26:15 -04:00
sandbox_details_round_trip.rs feat(sandbox): surface provider links and network policy (#275) 2026-05-16 10:16:18 -04:00
sandbox_services_round_trip.rs fix(server): improve sandbox service discovery 2026-05-10 12:40:38 -04:00
secret_metadata_round_trip.rs refactor(auth): split credential sources and vault schemas (#306) 2026-05-18 11:07:42 -04:00
secret_type_round_trip.rs refactor(auth): split credential sources and vault schemas (#306) 2026-05-18 11:07:42 -04:00
server_settings_round_trip.rs Collapsible run stage sidebar (#352) 2026-05-22 12:20:29 -04:00
session_contract_round_trip.rs feat(api): add ask fabro session endpoints (#342) 2026-05-21 21:26:15 -04:00
stage_completion_round_trip.rs refactor(run-projection): use stage vocabulary 2026-05-01 19:56:22 -04:00
stage_handler_round_trip.rs feat(api): expose stage handlers on run stages 2026-05-08 13:36:02 -07:00
stage_outcome_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
stage_projection_round_trip.rs feat: Replace duration/elapsed fields with wall_time_ms and StageTiming (#343) 2026-05-21 21:06:40 -04:00
stage_state_round_trip.rs refactor(api): tighten shared type schemas 2026-04-30 06:20:05 -04:00
status_round_trip.rs feat(api): unify public run shape 2026-05-10 20:48:55 -04:00
submit_answer_request_round_trip.rs fix(api): type interview answer submissions 2026-05-08 09:18:35 -07:00
workflow_settings_round_trip.rs feat: add run.checkpoint.skip_git_hooks to bypass Git commit hooks (#355) 2026-05-22 17:26:06 -04:00