Add the new destination key to [server.logging], list FABRO_LOG_DESTINATION
in the env vars table, and note that containers stream to stdout. Update
docs-internal/logging-strategy.md to describe the destination setting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add configurable server log destinations with an environment override so containers can stream foreground server logs to stdout while local installs keep file logging by default. Validate configured log filters at load time and reject stdout logging for daemon mode.
The strategy doc lived alone under files-internal/ while every sibling
strategy doc (logging, events, server-secrets) lived under docs-internal/.
Move it next to the others and update plan/spec references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `meta.stats: DiffStats` (required) to `PaginatedRunFileList` so the
Files Changed toolbar can render `+387 −104` next to the file count.
Server: refactors `list_binary_paths` into `list_diff_numstat`, which
returns the binary-path set plus aggregate `+/-` totals from a single
`git diff --numstat` invocation. The degraded patch-only response
populates the same field by counting `+`/`-` line prefixes in the
filtered patch (excluding `+++`/`---` file headers).
UI: `Toolbar` accepts `additions` / `deletions` and renders them as
mono-tabular `+387 −104` to the right of the file count. The block is
elided when the diff has 0 changes (e.g. binary-only or empty runs) so
the empty case stays clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a settings reference generator backed by OptionsMetadata on the sparse config layer structs. The generated user-configuration page is fenced and checked in CI alongside the CLI reference.
Add a cargo dev generator for the CLI reference and gate the generated docs in CI. The generator reads the fabro clap command tree through a narrow public reference surface so CLI docs drift is caught without exposing runtime command internals.
Wrap root CLI errors at the main boundary so fatal diagnostics use miette's styled renderer while preserving existing telemetry, exit codes, and auth help hints.
Move secret redaction and DisplaySafeUrl into fabro-redact so credential handling has a narrow ownership boundary. Update direct consumers and docs to depend on fabro_redact instead of fabro_util::redact.
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.
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.
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>
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>
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.
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>
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>
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>
Reconciles 61 origin commits (settings/config architectural reshape:
sparse layers → dense snapshots via builders, WorkflowSettings rename,
RunLayer/CliLayer moves, workflow builders, drop of public load wrappers)
with our LLM credential + RunServices refactor.
Our architecture preserved where it conflicted with origin's:
- RunServices / EngineServices stay (services.rs does not exist on
origin, which inlined the fields onto Initialized). Origin's new
Initialized fields (inputs, run_store, emitter, sandbox, registry,
env, dry_run, llm_client, provider) are absorbed through RunServices
and EngineServices instead of being inlined.
- llm_source: Arc<dyn CredentialSource> stays on AppState and
RunServices. Origin had a parallel ProviderCredentials struct in
fabro-server; our CredentialSource trait is more general and
complies with docs-internal/llm-client-resolution.md. Point-of-use
Client::from_source(...) rebuild preserves OAuth refresh.
- CommandContext.llm_source() uses self.storage_dir (origin's direct
field) instead of self.machine_settings (our side's field, removed
by origin).
- standalone_llm_source in fabro-agent drops the dead Result wrap and
uses fabro_config::user::default_storage_dir (origin's entrypoint)
instead of the removed load_settings_user/resolve_storage_root.
Absorbed from origin wholesale:
- SettingsLayer → WorkflowSettings rename everywhere
- Dense run settings: RunOptions.settings is WorkflowSettings, inputs
read via settings.run.inputs directly (not Option<RunLayer>)
- AppState.manifest_run_defaults / manifest_run_settings
- fabro_config re-exports of CliLayer/RunLayer/CliOutputLayer/etc.
- Lifecycle terminal-event changes, finalize dedup, list_events
consolidation — already brought in on the previous merge, kept
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge origin's fabro-config types boundary refactor (dense settings
migration: WorkflowSettings/UserSettings/ServerSettings moved to
fabro-types; SettingsLayer made pub(crate) inside fabro-config) into
local PR-refactor branch.
Conflict resolution intent:
- lib/crates/fabro-cli/src/commands/pr/{create,mod}.rs — kept HEAD's
server-side PR command implementations; origin still carried the
pre-refactor client-side helpers (build_github_credentials,
load_pr_record, branch_exists pre-check) that local commits had
already migrated to the server.
- lib/crates/fabro-cli/src/user_config.rs — took origin's resolution
(load_resolved_settings_from_toml + storage_dir_from_document tests),
which implements the same dead-storage_dir-wrapper cleanup local had
done via local_server::storage_dir.
- lib/crates/fabro-server/src/server.rs — kept HEAD's PullRequestRecord
import alongside origin's added ServerSettings; rewrote test helpers
github_token_settings + create_github_token_app_state to use origin's
ServerSettingsBuilder + AppStateConfig dense-settings shape (replaces
HEAD's parse_settings_layer + Arc<RwLock<SettingsLayer>>); switched
RunSpec.settings fixture from SettingsLayer::default() to
WorkflowSettings::default() per origin's RunSpec retype.
- Suppressed dead_code on CommandContext::storage_dir() and
::server_settings() (added by origin for use by client-side PR
commands that no longer exist after local's server-side migration);
gated load_resolved_settings_from_toml on cfg(test).
Verified post-merge: workspace fmt clean, clippy --all-targets
-D warnings clean, cargo nextest run --workspace 4587 passed,
182 skipped.
Replace hand-built 409 response that inlined {errors,pull_request} with
a regular ApiError::with_code(CONFLICT, ..., "pull_request_exists") and
drop the optional pull_request field that had been added to the
ErrorResponse OpenAPI schema solely to carry the existing record.
Clients receiving a 409 can GET /runs/{id}/pull_request to retrieve the
stored record when they need it — the detail string still includes the
existing html_url, which is the field most clients branch on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reconciles origin's "emit terminal event from FINALIZE" refactor
(41c47dbe1, e8a89ac39, 904c8842f) with the local RunServices refactor.
finalize() now performs origin's single list_events walk for stage
durations + artifact count, origin's compute_final_patch, deduped
stages/billing via billing_from_checkpoint, and origin's terminal event
emission — but reads run_store/sandbox/emitter from the shared
RunServices instead of individual Retroed fields. services.emitter.notice
replaces origin's local emit_run_notice helper.
test_support's execute_and_emit_terminal (added by origin) now accesses
run_store/emitter via executed.engine.run.* since Executed bundles
EngineServices. execute/tests.rs drops the terminal-event status
assertion origin deleted — status is no longer set at EXECUTE end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move PullRequestDetail, PullRequestGithubDetail, PullRequestUser,
PullRequestRef, and MergeMethod into fabro-types. Register them as
fabro-api with_replacement targets so the OpenAPI client and the server
share one canonical type per concept.
PullRequestDetail composes a stored PullRequestRecord with a flattened
PullRequestGithubDetail mirroring GitHub's REST payload, removing the
hand-rolled pull_request_detail_json builder in the server. Change the
PullRequestRef wire field from `ref_name` to `ref` so the same Rust
type round-trips through both GitHub and our API without aliases.
The server now uses fabro_api::types::{Create,Merge,Close}* directly,
deleting the hand-defined request/response shadows and the
`body.method.parse::<...>()` call (the typed MergeMethod enum drives
deserialization). Drops fabro-cli's `i64::try_from(record.number)`
panic path and the AutoMergeMethod enum (replaced by MergeMethod).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Integrates origin's worker-JWT-auth work (commits 8a6f83bb0..c847a828d)
with the config-boundary refactor that landed locally. Conflicts
resolved:
- commands/dump.rs: take origin's removal of the 500-line in-process
test block (replaced by real-server integration coverage).
- commands/run/runner.rs: keep local's dense WorkflowSettings import,
drop dead SettingsLayer import, pull in origin's ActorRef.
- manifest_builder.rs: adopt origin's lifted working_directory
resolution (fixes#159 - manifest git detection in nested repos),
but via local's resolve_working_directory_from_run API that takes
the dense RunNamespace. Update the regression test's
ManifestBuildInput literal to local's run_overrides/cli_overrides
field shape.
- server.rs: keep origin's jwt_auth_mode/jwt_auth_state/
test_user_subject/issue_test_user_jwt/issue_test_worker_token/
create_run_with_bearer/bearer_request test helpers, adapt
jwt_auth_state to local's create_test_app_state_with_session_key
signature (ServerSettings + RunLayer), keep local's dense
canonical_origin_settings that returns ServerSettings via
server_settings_from_toml. Rewrite
build_app_state_requires_session_secret_for_worker_tokens against
the dense AppStateConfig (resolved_settings +
resolved_runtime_settings_for_tests).
Post-merge verification: workspace builds clean, cargo +nightly
fmt --check all clean, cargo +nightly clippy --workspace
--all-targets -- -D warnings clean, cargo nextest run --workspace
4560 tests passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Return the existing PullRequestRecord on 409 from POST /runs/{id}/pull_request
so structured clients can recover the URL/number without a follow-up call.
The response now includes both the error envelope and a pull_request field
(same shape precedent as /install/finish's leftover_env_keys).
- Add server tests for merge/close error paths: 404 no_stored_record, 400
unsupported_host, 503 integration_unavailable, 400 invalid_merge_method, 502
github_not_found.
- Add a dedicated regression test proving the PR handlers use the
github_api_base_url captured at AppState construction, not a request-time
env read (SSRF defense invariant from the plan).
- Add an upgrade hint on unstructured 404s from the new PR client methods so
a new CLI against an old server sees "Upgrade the fabro server" instead of
an opaque failure.
- Refresh the stale CLI docs paragraph so it describes server-side GitHub
credentials, matching the post-refactor reality.
- Regenerate the TypeScript API client (had fallen behind the prior OpenAPI
schema additions) and add PullRequestRecord to ErrorResponse as an optional
field for the 409 case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was linking to https://fabro.dev/getting-started/quick-start (wrong
domain, 404). Use relative /getting-started/introduction so it resolves
correctly on docs.fabro.sh.
Fixes#167
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>