`--local` is supposed to render the settings that apply to the local CLI
/ client side, so it has no business resolving server settings. Drop the
server section and the warning path, return only project/workflow/run/
cli/features. Removes the boundary violation that was about to break the
CI boundary check, and restores the legacy_*_silently_ignored tests to
their original silent assertion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the plan at
docs/plans/2026-04-20-003-refactor-unify-run-vocabulary-metadata-plan.md.
- Rename RunRecord to RunSpec and RunProjection.run to .spec everywhere
in Rust source, tests, helpers, test names, and error messages.
- Introduce SerializableProjection wrapper that trims bulky node text
fields (prompt, response, diff, stdout, stderr) for run.json snapshots.
- Collapse metadata-branch and CLI export to one RunDump::from_projection
builder emitting run.json + graph.fabro + stages/{stage_id}/... and
drop legacy top-level start/status/checkpoint/sandbox/retro/conclusion
split files.
- Replace MetadataStore::write_checkpoint with write_snapshot returning
the commit SHA; add read_run_projection/read_run_spec; demote
read_checkpoint/read_start_record to projection-field extractors.
- Switch fork, rewind, rebuild_meta, CLI rewind recovery, and retro
upload to read the unified projection layout.
- Add additive query methods on RunSpec and RunProjection.
Serde-level `alias = "spec"` shim dropped; `rename = "run"` retained to
keep the server API wire format stable per the plan's scope boundary.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2/3 of the std::fs lint initiative (Phase 1 refactors landed in
commit 9d1c0d98c).
clippy.toml additions (appended to disallowed-methods):
std::fs::read, read_to_string, write, read_dir, copy, canonicalize
std::fs::File::open, File::create, File::create_new
std::fs::OpenOptions::open
File::options was deliberately excluded — it returns an OpenOptions
builder with no syscall. OpenOptions::open is where the block happens.
Non-blocking std::fs items (metadata, exists, create_dir_all, remove_*,
rename, and all std::fs types) remain legal.
Annotation policy (per updated plan):
- Mixed async/sync production source: function- or statement-scoped
#[expect(...)] so future accidental Tokio-path regressions in the
same file still fire.
- Fully-sync production source, test modules, integration tests,
build.rs: file-level #![expect(...)].
- Every #[expect] has a specific reason identifying the sync context.
Annotations added in ~90 files across the workspace. Notable narrow
placements: fabro-server server.rs current_server_target,
build_disk_usage_response, create_test_app_state_with_session_key;
fabro-server install.rs read_to_string rollback snapshot;
fabro-sandbox local.rs list_recursive; fabro-agent cli.rs FOLLOW-UP on
the JSON-stdout writer; fabro-llm providers/common.rs FOLLOW-UP for
load_file_as_base64 (7 translator call sites; revisit if file:// URL
usage grows).
build.rs blanket allows: fabro-api/build.rs, fabro-util/build.rs.
Pre-existing unrelated nightly-clippy warnings fixed under scope:
fabro-sandbox sandbox_spec.rs (unused_imports, unused_async),
reconnect.rs (unused_variables, unused_async).
Verified: cargo +nightly-2026-04-14 clippy --workspace --all-targets
-- -D warnings passes; fmt clean; 4129/4131 tests pass (two known
flakes under parallel nextest load, both pass individually and are
unrelated to this change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Route server-owned logs to <storage>/logs/server.log from the start of
tracing, remove legacy home/config ownership paths, and fail fast when
a running legacy daemon is detected instead of silently proceeding.
This also adds the missing sink-resolution, truncate/append,
concurrency, legacy-config, and uninstall regression coverage for the
home/storage cleanup plan.
Running `fabro` with no subcommand now prints a short, colorized guide
highlighting the most important commands (install, doctor, repo init,
validate, preflight, run, logs, server start, secret set/list, sandbox
ssh/preview/cp) instead of clap's full --help dump.
`fabro --help` and `fabro help` still render clap's comprehensive
reference unchanged. The CLI makes the root subcommand optional and
intercepts the None case in main_inner before telemetry or logging
init; the empty command name also suppresses the "CLI Executed"
tracking event for this pseudo-command.
Includes an inline-snapshot IT test covering the full landing body and
updates four pre-existing usage-line snapshots (<COMMAND> → [COMMAND])
that reflect the now-optional subcommand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make `fabro settings` render dense resolved settings by default for local
inspection, add a resolved view to the server settings endpoint with an
explicit compatibility marker, and update tests plus generated API clients
to lock the new behavior.
Embed defaults.toml as a base settings layer and apply it when
materializing effective settings and resolving typed settings.
This also fixes partial CLI table merging so builtin fields survive
higher-precedence overrides, and updates the affected CLI tests and
snapshots.
Keep project config and checked-in workflows under .fabro so they stay out of
normal repo listings. Update config discovery, CLI project commands, fixtures,
docs, and checked-in workflow paths to use .fabro/project.toml and
.fabro/workflows/*.
No production deployments exist, so there's no need for migration shims.
Remove all six backwards-compat type aliases (AgentError, SdkError,
CoreError, GraphvizError, StoreError, FabroError) and migrate ~880
callsites to use the canonical Error name directly within each crate,
or qualified imports (e.g., `use fabro_llm::Error as LlmError`) for
cross-crate references. Also fix a pre-existing absolute-path clippy
lint in fabro-server error.rs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The rustfmt.toml uses nightly-only options (struct_field_align_threshold,
imports_granularity, etc.) so stable rustfmt silently skips them,
producing different output. Use cargo +nightly fmt going forward.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The error standardization lost the file path from parse error messages
when anyhow::Context was removed. Add path field to ParseSettings
variant so errors like "Failed to parse settings file at /path: ..."
include the file location. Also fix test that expected capitalized
"Workflow not found" to match the new lowercase error message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve every clippy warning across the workspace when running with
--tests enabled. Previously only library code was lint-clean; test
code had accumulated issues that were invisible without --tests.
Fixes:
- redundant_closure_for_method_calls: |s| s.as_source() -> InterpString::as_source
(effective_settings, resolve_cli/root/server/features, run_event/record_serde,
materialize_run) — add InterpString imports where needed
- absolute_paths: inline fabro_types::settings::* paths -> use imports;
add #![allow(clippy::absolute_paths)] to fabro-cli and fabro-server
IT test harnesses (matching the existing pattern in integration.rs)
- bool_assert_comparison: assert_eq!(x, true) -> assert!(x)
- needless_raw_string_hashes: r#"..."# -> r"..." where no inner quotes
- field_reassign_with_default: mut + field assign -> struct literal with ..Default
- match_same_arms: merge Timeout | Disconnected arms in attach.rs
- needless_pass_by_value: signal_rx by ref in attach.rs
- unreadable_literal: 9999999999 -> 9_999_999_999
- default_trait_access: Default::default() -> BTreeMap::default()
- items_after_statements: move use to function top
- large_futures: allow in integration.rs test module (test-only, not prod)
- filter_map_bool_then: .filter_map(bool::then) -> .filter().map()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`--goal-file` was broken in the v2 path: `TryFrom<&RunArgs> for ConfigLayer`
did `let _ = &args.goal_file;`, so clap accepted the flag listed in
`--help` and then silently dropped it. Users running
`fabro run demo --goal-file prompts/goal.md` ended up with no goal at
all (or the DOT graph-level fallback), a regression from the legacy
flat `Settings` shape.
This commit adds first-class support for both inline and file-sourced
goals via a tagged union on `run.goal`. Greenfield decisions:
- **Single field, two variants.** `RunGoalLayer` is an untagged enum
of `Inline(InterpString)` and `File { file: InterpString }`. Makes
`goal XOR goal_file` un-representable in the type system and lets
the v2 merge matrix treat `run.goal` as a single scalar
(last-writer-wins) instead of needing a custom mutual-exclusion
merge rule. Matches the existing `DaytonaDockerfileLayer` pattern.
- **Relative paths are anchored at the file that declared them.**
`ConfigLayer::load(path)` walks the just-parsed `SettingsFile` and
rewrites any literal relative `run.goal.file` path to absolute
using `path.parent()` as the base, via new
`fabro_config::config::resolve_goal_file_paths`. CLI-sourced paths
via `--goal-file` are anchored at CWD in
`overrides::goal_layer_from_args`. Env-interpolated paths
(`${env.GOALS_DIR}/goal.md`) are left unresolved until consume time
and then resolved against the run's working_directory.
- **New accessors, no shims.**
- `run_goal_layer() -> Option<&RunGoalLayer>` — raw variant access.
- `run_goal_inline_str() -> Option<String>` — inline-only, returns
`None` for file-sourced goals.
- `resolve_run_goal(base_dir) -> Result<Option<ResolvedRunGoal>>` —
reads the file from disk if needed, returns text + provenance
(`ResolvedGoalSource::Inline | File { path }`).
- New `ResolveGoalError` enum covers env-lookup and I/O failures.
- Old `run_goal() / run_goal_str()` are **deleted** outright; every
call site has been updated to pick the right variant.
- **CLI wiring (the actual bug fix).** `overrides::goal_layer_from_args`
replaces the two `let _ = &args.goal_file;` lines with real
resolution: `(Some(text), None)` → `Inline`, `(None, Some(path))` →
`File { file: absolute }`. Both-set is rejected by a helper error
and clap already had `conflicts_with = "goal"` as a belt-and-
braces check. Applied to both `RunArgs` and `PreflightArgs`.
- **Manifest builder.** `resolve_manifest_goal` now calls
`args_layer.as_v2().resolve_run_goal()` and
`settings.resolve_run_goal()` in precedence order, then falls
through to the graph-level `@file` sugar if both are absent. The
resolved goal is translated to a `ManifestGoal { text, type_, path }`
by a new `resolved_goal_to_manifest` helper — inline goals get
`type = Value`, file-sourced goals get `type = File` with the
absolute path echoed for provenance.
- **Workflow pipeline.** `fabro-workflow::operations::source::
resolve_goal_override` is rewritten to use `resolve_run_goal`
against the working_directory. The orphaned helper `resolve_goal_file`
(a stub from Stage 4 that was always called with `None`) is
deleted.
- **Server-side manifest.** `fabro-server::run_manifest::
prepare_manifest` stores the CLI-resolved goal as
`RunGoalLayer::Inline`, matching the Stage 4 plan's "CLI owns goal
file reads; server never touches the filesystem for goals"
contract.
## Tests
**Schema** (`fabro-types::settings::accessors`):
- `run_goal_inline_str_returns_source_value` — literal inline variant
- `run_goal_inline_str_is_none_for_file_variant` — file variant
explicitly yields `None` from the inline accessor
- `resolve_run_goal_reads_file_variant_from_disk` — end-to-end file
read with provenance assertion
- `resolve_run_goal_inline_passes_text_through` — inline passthrough
**Config load** (`fabro-config::config`):
- `parse_accepts_inline_goal` + `parse_accepts_file_variant`
- `parse_rejects_goal_with_unknown_sibling_fields` — untagged enum
correctly rejects mixed-shape TOML
- `combine_replaces_file_goal_with_inline_from_higher_layer` and the
reverse — confirms the tagged union merges as a single scalar with
no custom rule needed
- `load_rewrites_relative_goal_file_to_absolute`
- `load_leaves_absolute_goal_file_untouched`
- `load_leaves_env_interpolated_goal_file_untouched`
**CLI overrides** (`fabro-cli::commands::run::overrides`):
- `goal_and_goal_file_together_is_rejected`
- `goal_file_is_anchored_at_cwd_when_relative`
- `absolute_goal_file_is_preserved`
- `inline_goal_builds_inline_variant`
- `empty_args_produce_no_goal_layer`
**CLI integration** (`fabro-cli::tests:🇮🇹:cmd::run`):
- `dry_run_with_goal_file_reads_contents_into_goal` — end-to-end
`fabro run --dry-run --auto-approve --goal-file <path>` and asserts
the file contents appear in the preflight summary. Explicit
regression test for the silently-ignored flag.
- `dry_run_rejects_goal_and_goal_file_together` — clap conflicts_with
## Callsite churn
Every `run_goal() / run_goal_str()` call site updated:
- `fabro-config/src/effective_settings.rs` — 2 test assertions →
`run_goal_inline_str()`
- `fabro-cli/tests/it/cmd/{config,create}.rs` — 3 sites → inline
- `fabro-cli/src/manifest_builder.rs` — rewritten to use
`resolve_run_goal`
- `fabro-workflow/src/operations/create.rs` — 2 sites, test + set
- `fabro-workflow/src/operations/source.rs` — rewritten
- `fabro-server/src/{run_manifest,server}.rs` — set + test assertion
3,782 workspace tests pass (was 3,765, +17 new). `cargo fmt
--check --all` and `cargo clippy --workspace -- -D warnings` are
clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Final mechanical pass: replaces every remaining
`fabro_types::settings::v2::*` import path with
`fabro_types::settings::*` (or the appropriate submodule) across 53
files in 10 crates, then deletes the transitional
`pub mod v2 { pub use super::*; }` alias from
`fabro-types/src/settings/mod.rs`.
No functional changes — all touches are `sed s|settings::v2::|settings::|g`
on import statements and fully-qualified type paths. The v2
namespace is now fully gone; the authoritative module path is
`fabro_types::settings::{accessors, cli, duration, features, interp,
model_ref, project, run, server, size, splice_array, tree, version,
workflow}`.
All 3,758 workspace tests pass. `cargo fmt --check --all` and
`cargo clippy --workspace -- -D warnings` are clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the Stage 6.2 stopgap `strip_nulls(serde_json::to_value(full
SettingsFile))` path in `get_server_settings` with an explicit
redaction pass in the new `fabro_server::settings_view` module.
The redaction drops the narrow set of fields that leak operational
secrets or host filesystem layout:
- `server.listen.*` (bind + TLS material)
- `server.auth.api.jwt.{issuer, audience}` (auth topology)
- `server.auth.api.mtls.ca` (filesystem path)
- `server.auth.web.providers.github.client_secret`
Every other field is preserved. `InterpString` values that reference
`${env.NAME}` already serialize to their unresolved template form, so
no additional env-provenance walk is needed in this pass.
Implements the real `/api/v1/runs/:id/settings` handler — previously
wired to `not_implemented` — by opening the run reader, reading the
persisted `RunRecord.settings`, running it through the same
redaction, and serializing. The demo route still points at
`demo::get_run_settings`, unchanged.
Updates `fabro-cli` to deserialize the new wire shape as
`SettingsFile` directly:
- `server_client::retrieve_server_settings` now returns
`SettingsFile` (no longer the legacy flat `Settings`) by decoding
the progenitor `types::ServerSettings` newtype map into a
`serde_json::Value` and then into `SettingsFile`.
- `commands/config/mod.rs::legacy_settings_to_v2` shim (TODO-1)
**deleted**; `merged_config` passes the v2 file straight into
`effective_settings::resolve_settings`.
- The `fabro-cli` integration tests rewrite their mock `/api/v1/settings`
payloads as v2 TOML via `ConfigLayer::parse` instead of hand-rolling
the legacy TOML shape.
All 3,761 workspace tests pass. `cargo fmt --check --all` and
`cargo clippy --workspace -- -D warnings` are clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bridge.rs (818 LOC) is gone. Production consumers no longer produce a
full legacy `Settings` from v2 state; every read path walks the v2 tree
directly or uses one of the narrow v2->runtime helpers in the new
`settings::v2::to_runtime` module.
Core moves:
fabro-types
- Delete `settings::v2::bridge::bridge_to_old` and the whole bridge.rs
file.
- Relocate the narrow v2->runtime helpers (`bridge_sandbox`,
`bridge_mcp_entry`, `bridge_mcps`, `bridge_hook`, `bridge_worktree_mode`,
`bridge_merge_strategy`, `bridge_pull_request`, `bridge_run_artifacts`)
into a new `settings::v2::to_runtime` module. Each helper takes a
single v2 subtree and produces the corresponding runtime shape;
nothing assembles a full legacy `Settings` anymore.
- `settings/mod.rs` doc comment rewritten to describe `Settings` as a
runtime shape, not a resolved parse target. Stage 6.3 deletes it.
fabro-config
- `ConfigLayer::resolve` is gone along with the `TryFrom<ConfigLayer>
for Settings` impls. Consumers call `.into()` for a `SettingsFile`,
or `.as_v2()` to borrow one.
- `fabro_config::server::resolve_storage_dir` now takes `&SettingsFile`.
fabro-server
- `api_server_settings` emits the v2 `SettingsFile` JSON shape
directly instead of bridging to the legacy flat DTO. Stage 6.6
replaces the shape again with an explicit allow-list DTO.
- `serve.rs`: `load_settings` returns `SettingsFile`;
`apply_serve_overrides` / `apply_runtime_settings` mutate v2
subtrees directly; `build_artifact_object_store` walks
`server.artifacts`; `build_legacy_api_settings` projects the v2
auth/listen/api subtrees down to the legacy `ApiSettings` shape for
the (still-legacy) auth resolver.
- `diagnostics::check_crypto` walks `server.auth.api.{jwt,mtls}` and
`server.listen.tls` directly.
- `web_auth.rs` oauth / register / setup-status / auth-me flows all
read `server.web`, `server.integrations.github`, and
`server.auth.web` directly via the v2 accessors. `merge_settings_keys`
now writes v2 TOML (with `[server.web]`, `[server.integrations.github]`,
etc.) instead of the legacy v1 top-level keys, and the register
handler re-parses the freshly-written file back into the in-memory
`SettingsFile` state.
fabro-cli
- `CommandContext::machine_settings` returns `&SettingsFile`.
- `user_config::load_settings` and friends return `SettingsFile`.
- `user_config::resolve_server_target` / `exec_server_target` /
`configured_server_target` walk `cli.target.{http,unix}` directly.
Tests rewritten against v2 TOML fixtures.
- `main.rs` logging init reads `cli.logging.level` / `server.logging.level`
via v2 accessors.
- `commands/exec.rs` reads `cli.exec.{model,agent}` and builds mcps
from `cli.exec.agent.mcps` (falling back to `run.agent.mcps`) via
`to_runtime::bridge_mcp_entry`.
- `commands/pr/mod.rs` calls `github_app_id_str()`.
- `commands/run/create.rs` drops the legacy `.resolve()` call and uses
`Into::<SettingsFile>::into(...)`.
- `commands/config/mod.rs::legacy_settings_to_v2` is now a real
reverse-mapping helper that covers `storage`, `scheduler`,
`integrations.{github,slack}`, `run.model`, `run.inputs`, and
`cli.output.verbosity`. Stage 6.6 deletes it when the API client
returns v2 natively.
- `tests/it/cmd/config.rs` tests now walk the v2 tree directly (via
`cfg.run_model_name_str()`, `cfg.run_inputs()`, `cfg.run_sandbox()`,
`cfg.run_hooks()`, `cfg.run_agent_mcps()`, `cfg.run_prepare_commands()`,
`cfg.server_storage_root_str()`, etc.). The `bridge_to_old` test
helper is gone.
- `tests/it/api/settings.rs` asserts against the v2 JSON shape.
Build, test, and quality gates all green:
- `cargo build --workspace --tests`
- `cargo clippy --workspace -- -D warnings`
- `cargo fmt --check --all`
- `cargo nextest run --workspace`: 3758 / 3758 passed, 182 skipped.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Completes the fabro-cli test migration for Stage 6.1. Every test in
`cargo nextest run --workspace` now passes (3,764 passed / 0 failed).
Changes:
- cmd/support.rs: compact_inspect / compact_git_inspect now walk the
v2 tree (/settings/run/goal, /settings/run/sandbox/provider,
/settings/run/model/provider) and derive `dry_run` from the v2
execution mode.
- cmd/attach.rs: the event-log filter strips _version and redacts
settings.cli.target.path to [CLI_SOCKET] so randomized tempdir
sockets don't pollute the snapshot. Insta snapshot accepted.
- cmd/run.rs: same cli.target redaction in the run event filter.
dry_run_persists_event_history_in_store and
json_run_implies_auto_approve_for_human_gates check for
`settings.run.execution.approval == "auto"` instead of
`settings.auto_approve == true`. Insta snapshot accepted.
- cmd/config.rs: parse_settings bridges the v2 YAML output back down
to the legacy flat Settings shape so the existing helper assertions
keep working. settings_fetches_server_settings_and_merges_with_local_config
now asserts the v2 R22 behavior (run.inputs replaces wholesale, so
server-side `server_only` is dropped in favor of project's vars).
settings_uses_fabro_home_for_home_config_resolution walks the v2
JSON paths (cli.output.verbosity, run.model.name).
create_explicit_workflow_path_uses_project_config_relative_to_workflow
asserts against the v2 run-record shape.
- fabro-cli/commands/config/mod.rs: legacy_settings_to_v2 is now a
real (if narrow) reverse bridge covering storage, scheduler, github
integration, slack integration, run.model, run.inputs, and cli
verbosity. Stage 6.6 still replaces this when the API client returns
v2 types natively, but for now the server-side defaults round-trip
through the resolver with enough fidelity to keep the settings
command integration tests honest.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Close out consumer migration with targeted behavior fixes and the
remaining integration-test fixture rewrites. The full workspace
nextest run now reports 3,760 passed / 0 failed / 182 skipped.
Runtime fixes:
- effective_settings::apply_server_defaults now propagates the full
server-side Settings shape (llm, sandbox, setup, checkpoint,
pull_request, artifacts, hooks, mcp_servers, github, slack, fabro)
into the resolved CLI settings, matching the pre-Stage-3 'merge
everything server' behavior for RemoteServer/LocalDaemon modes
- fabro-cli commands/run/overrides: route --verbose through
cli.output.verbosity = verbose instead of a run.metadata stash,
so it resolves to settings.verbose via the bridge
- fabro-server run_manifest manifest_args_layer: same — emit a
CliLayer with cli.output.verbosity rather than stuffing the flag
into run.metadata
- fabro-test settings_storage_dir: detect the managed marker and
return None instead of parsing the injected server.storage.root,
so isolated_server correctly spins up a new storage dir
- fabro-server run_manifest_local_daemon test now passes with full
server-side settings snapshot propagation
Test fixture + assertion updates:
- cmd::config::settings_local_explicit_workflow_path_uses_workflow_project_layers:
assertion updated for v2 R30 whole-list replacement of
run.prepare.steps across layers (only workflow-setup survives)
- cmd::config::create_explicit_workflow_path_uses_project_config_relative_to_workflow:
same correction for the persisted run.settings.setup.commands
- cmd::attach::attach_json_errors_without_prompting_for_human_input
and cmd::run::json_run_implies_auto_approve_for_human_gates: strip
the bridge-emitted settings.server and settings.version fields from
the JSON snapshot so the randomised unix-socket path does not flap
the insta snapshot
- cmd::server_start::concurrent_autostart_converges_on_one_shared_daemon_and_cleans_up:
rewrite the injected settings.toml to v2 shape with
[server.storage] root and [cli.target] type = unix path
- scenario::smoke::attach_smoke_covers_arg_validation_and_remote_server_behaviors:
two [server] target fixtures rewritten to [cli.target]
type = http url
Accepted insta snapshots for attach and run JSON outputs. Workspace
build + clippy both clean under -D warnings.
The old HookDefinition struct has HookType flattened via
#[serde(flatten)], so emitting hook_type = Some(HookType::Command {...})
produces an inner 'command' key at the same level as the outer
HookDefinition.command shorthand field. Round-tripping through YAML
then fails with 'duplicate field command'.
Bridge script/command hooks via the HookDefinition.command shorthand
instead, leaving hook_type = None. Also: sandbox FABRO_CONFIG in the
manifest_builder unit test so it doesn't pick up the developer's real
~/.fabro/settings.toml, and update settings_local_merges_cli_and_project_defaults
to reflect v2 R22 semantics: run.inputs replaces wholesale across
layers rather than merging by key, while daytona.labels stays a sticky
merge-by-key map per R71.
Collapse expensive CLI smoke coverage into scenario tests, replace the
slow doctor no-color integration check with a unit-level render test,
and remove duplicate attach coverage. Also fix local Unix-socket
autostart so missing daemons don't spend the full 5s readiness wait
before startup.
The commit includes the measured slow-test report updates for the work
landed here.
Default `fabro settings` now resolves effective runtime settings against the
selected server, while `--local` preserves local-only inspection. This also
extracts shared settings resolution logic so CLI output, manifest preparation,
and the `/api/v1/settings` contract stay aligned.
Keep local server targeting based on explicit server targets instead of
implicitly deriving a socket from storage_dir. This makes ~/.fabro/fabro.sock
the default local socket again, keeps storage under ~/.fabro/storage, threads
FABRO_CONFIG through server autostart paths, and updates the CLI test harness
for the new split.
- centralize FABRO_HOME and storage path resolution in fabro-config
- rename store types, extract ArtifactStore, and simplify run key layout
- switch run scratch to scratch/, remove RuntimeState, and refresh docs/clients
Consolidate CLI and server machine defaults under settings.toml,
including loader renames, writer preservation fixes, same-machine
manifest handling, and docs/test updates for the new config model.
Move --storage-dir and --server-url off GlobalArgs and onto the
leaf commands that actually honor them.
This aligns help, parser behavior, and env-var wiring with the
current command architecture while preserving the intended model
and exec targeting semantics.
Move durable run access and execution control onto the server-backed client,
canonicalize run APIs under /api/v1/runs, and switch CLI integration tests
to a shared test daemon/storage model with shared-state-safe assertions.
InMemoryStore duplicated SlateStore's interface and was unused in
production. RunSnapshot/NodeSnapshot were intermediate projections that
tests consumed — replaced with RunState to eliminate the indirection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The server subcommand and related code were gated behind
cfg(feature = "server"). This removes the feature flag entirely,
making fabro-server a required dependency so the server command
is always available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move storage_dir() from FabroSettingsExt trait in fabro-config into an
inherent method on Settings in fabro-types. Remove the re-export from
fabro-config so callers import directly from fabro_types. Drop the
redundant Fabro prefix since the type already lives in the fabro_types
crate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make the full workspace nextest run reliable after the run-store migration,
restore legacy test-harness projections needed by workflow integration tests,
and clear the remaining fmt/clippy issues in the touched paths.