Commit graph

2855 commits

Author SHA1 Message Date
Bryan Helmkamp
f99011fdb0
style(install): blank line before install token in start banner
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:54:01 -04:00
Bryan Helmkamp
92566d430f
Merge remote-tracking branch 'origin/main' 2026-04-24 13:39:42 -04:00
Bryan Helmkamp
828a8c4429
refactor(redact): make credentialed URL logging safe
Add DisplaySafeUrl under fabro-util::redact so URL Display and Debug output redact credentials by default. Migrate token-bearing GitHub, OAuth, server, LLM, sandbox, and workflow paths to use the wrapper at logging/error boundaries while keeping raw URLs explicit for wire and shell transit.
2026-04-24 13:39:34 -04:00
Bryan Helmkamp
637eb8ba1c
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-server/src/install.rs
2026-04-24 13:25:21 -04:00
Bryan Helmkamp
ad18696894
fix(workflow): prevent signed sandbox checkpoint commits
Disable git signing for sandbox bookkeeping commits and surface timeout or killed git operations with actionable error text.
2026-04-24 13:22:17 -04:00
Bryan Helmkamp
701598a472 feat(install): show copyable install token in server start banner
Color the install URL and add a separate block that prints the install
token on its own line so users can copy it without parsing the query
string. The token block shows in both the URL and reverse-proxy branches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:04:02 -04:00
Bryan Helmkamp
68214575e3 docs: refresh changelog and setup docs
Add dated changelog entries for recent user-facing changes and update server setup docs for the object-store installer flow.
2026-04-24 13:00:15 -04:00
Bryan Helmkamp
8aeaef5f33 fix(install): mount wizard at root route 2026-04-24 12:59:16 -04:00
Bryan Helmkamp
c6ae3b8407 Merge remote-tracking branch 'origin/main' 2026-04-24 12:44:36 -04:00
Bryan Helmkamp
533785cd4c fix(install): expose local object store root 2026-04-24 12:38:04 -04: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
b975a802dc fix(install): show AWS access key ID as plain input
The AWS access key ID is not a secret — swap its PasswordInput for a
regular text input so operators can read and edit it directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:29:27 -04:00
Bryan Helmkamp
6d97de0d99 docs(quick-start): document supported platforms
Add a Supported platforms table to the quick-start so Intel Mac and
Windows users learn they're unsupported before running the installer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:25:40 -04:00
Bryan Helmkamp
7d791c531b fix(install): use single-line install token input
Refresh the embedded SPA assets so the Rust bundle serves the updated install screen.
2026-04-24 12:20:17 -04:00
Bryan Helmkamp
333657481d Merge remote-tracking branch 'origin/main' 2026-04-24 12:14:24 -04:00
Bryan Helmkamp
b2f8e0fb75
refactor(workflow): simplify fork rewind cleanup
Extract shared checkpoint CLI helpers and run-store error mapping, and remove redundant rewind/fork state from the workflow operations.
2026-04-24 12:13:09 -04:00
Bryan Helmkamp
68c9ddec5e
build(spa): refresh embedded web assets
Regenerate the tracked SPA bundle so the TypeScript build job's refresh-and-diff gate stays clean.
2026-04-24 12:02:39 -04:00
Bryan Helmkamp
e4e5eb08d7
plan 2026-04-24 11:45:23 -04:00
Bryan Helmkamp
76c7124be7
plan: adopt six engineering patterns from uv
New deep plan covering DisplaySafeUrl (fabro-redacted), EnvVars
registry (fabro-static), expanded snapshot helpers, miette CLI
diagnostics, fabro-dev unified CLI, and OptionsMetadata-driven docs
generation. Six phases, thirteen implementation units, ships as
independent PRs.

Includes deepening-pass revisions and reviewer feedback:

- Clippy enforcement reframed as workspace-wide bans with crate-level
  #![allow] opt-outs (clippy.toml has no per-crate scoping).
- Phase 4 (miette) narrowed: fancy rendering + help: footer only; no
  source-highlighting promise since no fabro error type carries spans.
  main() retains its telemetry/shutdown lifecycle.
- Phase 6 split into 6.2 (cli.mdx) and 6.3 (user-configuration.mdx)
  with fenced-region commitment upfront; resolves the previously
  orphaned user-configuration drift problem.
- Phase 2 gains an explicit allow/deny list for DisplaySafeUrl use
  and neutralizes the .to_string() trap via explicit .redacted_string()
  / .raw_string() methods plus a clippy deny on the implicit path.
  install.rs:1227,1546 and fabro-cli/commands/server/mod.rs:266-270
  are explicitly kept as raw String (Location headers, user-facing
  install URLs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:45:23 -04:00
Bryan Helmkamp
4215ed3c16
refactor(workflow): make rewind fork and archive
Rewind now creates a resumable replacement run from the selected checkpoint, archives the source run, and records run.superseded_by for auditability. Fork, rewind, and timeline listing now share server-backed git-store plumbing, with generated API clients and docs updated for the new contract.
2026-04-24 11:42:37 -04:00
Bryan Helmkamp
22f0f8122f
Merge remote-tracking branch 'origin/main' 2026-04-24 11:32:42 -04:00
Bryan Helmkamp
6cf58d381f
ci: remove check-boundary.sh
No longer invoked by CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:31:21 -04:00
Bryan Helmkamp
23a317d4a1
test(server): align PR endpoint test with LLM credential source 2026-04-24 11:31:04 -04:00
Bryan Helmkamp
ea85785b41
ci: remove Boundary job
No longer needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:30:34 -04:00
Bryan Helmkamp
baa20ca375
Merge remote-tracking branch 'origin/main' 2026-04-24 11:23:34 -04:00
Bryan Helmkamp
0a297b26bf
refactor(pr): simplify server-side PR plumbing
Reuse the existing merge strategy type across CLI/API/GitHub paths, consolidate repeated PR command setup, and serialize server-side PR creation per run to avoid duplicate external work.
2026-04-24 11:17:01 -04:00
Bryan Helmkamp
ac4f306cc6
Merge pull request #168 from fkukuck/fix/auto-pr-resolved-client
fix(workflow): reuse resolved llm client for auto-pr
2026-04-24 11:10:25 -04:00
Bryan Helmkamp
94471a6a3d
docs: fix Mintlify MDX parsing 2026-04-24 10:59:25 -04:00
Bryan Helmkamp
9ede01698c
refactor(server): narrow RunPrInputs to goal; drop PullRequestGithubContext.{owner,repo}
Two code-reuse findings from the simplify review:

1. PullRequestGithubContext carried owner/repo String fields obtained by
   re-parsing record.html_url, even though PullRequestRecord already
   carries typed non-optional owner/repo fields. Dropped the redundant
   fields; the 3 PR handlers read via &ctx.record.owner /
   &ctx.record.repo instead. The incidental non-github.com URL
   rejection is preserved as an explicit one-line host-validation
   call (documented by the rejects_non_github_record_url tests).

2. RunPrInputs held run_spec: &RunSpec purely to read goal()
   downstream. Narrowed to goal: &str stored directly; the server
   handler passes inputs.goal to OpenPullRequestRequest::from_run_state,
   which no longer needs the full RunSpec. Fewer fields, clearer
   dependency at the call site.

Also tightened the from_run_state doc comment (was narrating peer
callers' behavior rather than the method's contract).

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4581 passed, 182 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:38:55 -04:00
Bryan Helmkamp
aa82ef6da6
plan: resolve second-review findings and expand unit 2 scope
Apply the five findings from the external review: reject archived sources
with 409 (was contradictory); emit RunSupersededBy only on archive success
(was self-contradicting with the ordering rationale); look up working_directory
from the run's RunSpec instead of hand-waving AppState.repo_path; plumb
superseded_by through RunSummary + OpenAPI to honor the 'helps fabro ps' claim;
reconcile test scenarios to the archive-first ordering.

Also add GET /runs/{id}/timeline to Unit 2 so --list display moves server-side
alongside the mutating rewind call (web-UI parity). Normalize all status
codes from 412 to 409 to match fabro-server's CONFLICT convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:28:36 -04:00
Bryan Helmkamp
24fc4d77b7
plan: apply unit 2 adversarial review decisions
Record the five decisions from the targeted Unit 2 review: 207 Multi-Status
for archive-failure partial success, graceful-degradation mapping for TOCTOU
precondition races, archive-first event ordering, accept-orphan retry posture,
and a new superseded_by projection field. Also add spawn_blocking and
operations-layer composite guidance from the review's autofixes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:28:36 -04:00
Bryan Helmkamp
3a6a00f439
plan: converge rewind into fork with server-side endpoint
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:28:36 -04:00
Bryan Helmkamp
d6eb18f037
Merge remote-tracking branch 'origin/main' into fix/auto-pr-resolved-client 2026-04-24 09:33:28 -04:00
Bryan Helmkamp
f19c0e2e91
test(fabro-llm): remove trybuild compile-fail test 2026-04-24 09:30:55 -04:00
Bryan Helmkamp
d822af3d32
Merge remote-tracking branch 'origin/main' 2026-04-24 09:29:21 -04:00
Bryan Helmkamp
16184a6f96
refactor(github): drop merge_method.as_str() shim in PUT /pulls/:n/merge body
MergeMethod derives serde(rename_all = "snake_case"), so json!({
"merge_method": method }) emits the same `"squash"` / `"merge"` /
`"rebase"` strings as the as_str() round-trip. Inlining the typed value
removes the only remaining manual string conversion in the merge path.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4581 passed (fabro-github merge_pr unit
tests still pass — they assert against status codes not payload bytes,
but the twin-mode integration test create_merge_and_verify_state
exercises the on-the-wire JSON shape).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:23:18 -04:00
Bryan Helmkamp
961cb4c0c9
refactor(server): pr_test_app_with_completed_run for create-PR happy-path tests
The 4 callers of create_completed_run_ready_for_pull_request all paired
it with pr_test_app(...) and used identical defaults for base_branch
("main"), run_branch ("fabro/run/42"), and diff. Only repo_origin_url
varied per test. Bundle into pr_test_app_with_completed_run(token,
github_base_url, repo_origin_url) -> (state, app, run_id); each call
site shrinks from 12 lines to 1 helper invocation.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run -p fabro-server 439 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:20:26 -04:00
Bryan Helmkamp
c4404fc931
refactor(server): pr_test_app_with_minimal_run for create_run holdouts
Add an async sibling helper that bundles state + app + a fresh
create_run(&app, MINIMAL_DOT) into one (state, app, run_id) tuple.
Updated the 2 PR tests that had built this triple manually
(merge/close not_found_when_record_missing). The third holdout at
line 10148 keeps its own setup — it has an intervening
assert_eq!(state.github_api_base_url, github.base_url()) that
documents a load-bearing invariant about app state construction.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run -p fabro-server 439 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:17:54 -04:00
Bryan Helmkamp
12ca595f54
refactor(github): take GitHubContext by reference in public API
Public functions now take ctx: &GitHubContext<'_> instead of by-value
GitHubContext<'_>. Matches the surrounding &str / &GitHubCredentials
convention. The type stays Copy so internal call sites that pass `ctx`
through still work without explicit reborrows.

Touched: 8 fabro-github functions + matching _with_client variants,
plus call sites in fabro-server, fabro-workflow, fabro-sandbox, and
fabro-github's integration + unit tests. Pure mechanical change.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4581 passed, 182 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:15:25 -04:00
Bryan Helmkamp
896f0bb8ad
refactor(agent): add Session::from_source convenience constructor
Session keeps llm_client: Client as its internal model — a session is
bounded (≤ 1 hour) and its cached client stays fresh within that
window. Session::new(client, ...) remains the primitive (used by the
server-mediated agent adapter path in fabro-cli/exec.rs, which builds
a Client with a custom ProviderAdapter, no source involved).

Add Session::from_source(source, ...) for callers that hold a source
directly — resolves a Client via Client::from_source and delegates to
new. Lets workflow-level callers that store Arc<dyn CredentialSource>
build a Session without hand-resolving first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:08:37 -04:00
Bryan Helmkamp
b4bc9a0506
refactor(auth): extract ApiCredential::from_api_key
The "Anthropic uses x-api-key header, everyone else uses Bearer" logic
was written three times: env_source (env-based construction), resolve
(vault-based construction), and provider_auth (CLI key validation).
Any future header rename would need three edits.

Add ApiCredential::from_api_key(provider, key) as a canonical
constructor. Each callsite now builds via the helper and overrides only
the fields specific to its path (env base URLs, vault-sourced org/project
IDs, codex mode, etc.).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:05:54 -04:00
Bryan Helmkamp
7e83cd38e5
refactor(pipeline): split build_pr_body signature; drop RunServices::for_cli
build_pr_body and maybe_open_pull_request now take the two things they
actually need — run_store: &RunStoreHandle and llm_source: &dyn
CredentialSource — instead of services: &RunServices. The workflow
PULL_REQUEST phase decomposes services at the callsite; the standalone
fabro pr create command passes its own directly.

This removes RunServices::for_cli, a stub constructor that fabricated
an emitter, sandbox, and provider just to satisfy the RunServices type
for two fields it cared about. The "leaky fake" is gone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:02:13 -04:00
Bryan Helmkamp
042fca0dec
refactor(server): add pr_test_app fixture for PR endpoint tests
Add a pr_test_app(token, github_base_url) -> (state, app, run_id)
helper that bundles the create_github_token_app_state +
build_router(...) + fixtures::RUN_1 triple every PR-endpoint test
shared. Updated 15 call sites; the 3 tests that derive run_id from
create_run(&app, MINIMAL_DOT).await keep their own setup since they
need the app before the run_id exists.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4581 passed, 182 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:00:30 -04:00
Bryan Helmkamp
b01e08a52d
refactor(workflow): drop Concluded.run_id and pushed_branch
Both fields were derivable from run_options (run_options.run_id and
run_options.git.as_ref().and_then(|g| g.run_branch.clone())), so they
were a second place to keep in sync with the canonical source.

Drop both from Concluded, populate Finalized's copies from run_options
at the pull_request phase boundary. Add RunOptions::run_branch() helper
so the "reach into optional git opts" pattern reads as a single call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:57:50 -04:00
Bryan Helmkamp
336553e30f
remove(cli): drop pr list command entirely
Abandoning `fabro pr list`. Deletes:
  - lib/crates/fabro-cli/src/commands/pr/list.rs
  - lib/crates/fabro-cli/tests/it/cmd/pr_list.rs
  - PrListArgs struct + PrCommand::List variant + dispatch arm + name
  - The `client()` accessor + `client` field on ServerSummaryLookup
    (`pr list` was the only consumer)
  - `### fabro pr list` section in docs/reference/cli.mdx
  - `list` row + alias from the `fabro pr --help` snapshot test

Server side untouched: there was no `/pull_requests` endpoint to
remove. Historical changelog and plan docs left as-is — they record
when the command shipped, not its current existence.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4581 passed (down from 4584 by the
3 deleted pr_list tests), 182 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:56:07 -04:00
Bryan Helmkamp
35763dc437
refactor(workflow): inline RunServices::for_test into test_default
The for_test helper was a second layer of indirection — EngineServices
::test_default() called it, and it was the only caller. Inlining
collapses two test-scaffolding functions into one. The thread+runtime
scaffolding stays (it's still needed because create_run is async and
tokio tests can't block_on directly), just moves up one level.

Also drops the StubCredentialSource struct at module scope; it moves
inside test_default() since that's its only use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:54:56 -04:00
Bryan Helmkamp
58ce594f72
refactor: inline empty_pull_request_diff_error into RunPrInputs::extract
The helper had only one meaningful caller after the previous diff/conclusion
validation collapse. Inlining keeps the diff-validation message + error code
in the same place as the rest of RunPrInputs::extract's validation branches.
RunPrInputs is already grouped with the other PR helpers in server.rs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:48:43 -04:00
Bryan Helmkamp
1781788798
refactor: extract RunPrInputs validation; drop dead is_app_public
Two cleanups:

1. Extracted the 8 sequential let-Some-else-return validations from
   create_run_pull_request into a server-local RunPrInputs struct with
   an extract(&run_state, force) -> Result<RunPrInputs, ApiError>
   constructor. The handler shrinks from ~85 lines of validation +
   build to a single match RunPrInputs::extract(...) followed by
   creds + model + request build. All error codes/messages preserved.

2. Deleted is_app_public from fabro-github plus its 3 unit tests and
   the now-unused MockHeaderCheck::Missing / with_req_header_missing
   test-helper variants. No production caller remained after the
   server-side install flow stopped checking app visibility client-side.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4584 passed (down from 4587 by the 3
deleted is_app_public tests), 182 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:39:58 -04:00
Bryan Helmkamp
60e2027ac3
refactor: extend GitHubContext to remaining callers; add OpenPullRequestRequest::from_run_state
Two cleanups:

1. Threaded GitHubContext through the remaining fabro-github functions
   that pair credentials with the API base URL: branch_exists,
   resolve_clone_credentials, resolve_authenticated_url. Each loses its
   trailing `base_url: &str` and replaces `creds: &GitHubCredentials`
   with `ctx: GitHubContext<'_>`. is_app_public was skipped — it doesn't
   take credentials. Updated production callers in fabro-sandbox/daytona
   and fabro-workflow/sandbox_git, plus integration and unit tests.

2. Added OpenPullRequestRequest::from_run_state on the workflow struct.
   Bundles the validated unpacked-from-RunState pieces into a draft PR
   request with the server's defaults (`draft = true`, `auto_merge =
   None`). Server's create_run_pull_request handler now calls the
   constructor instead of inlining a 12-field struct literal — the
   handler reads as a sequence of validations followed by one named
   request build, not as plumbing.

Verified: workspace fmt clean, clippy --all-targets -D warnings clean,
cargo nextest run --workspace 4587 passed, 182 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:31:14 -04:00
fabro-releases[bot]
825dff6142 Bump version to 0.213.0-nightly.0 2026-04-24 09:45:14 +00:00