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.
Stage 3 of the settings TOML redesign. Switches the core parse/merge/
resolve path to the v2 namespaced schema while keeping the legacy flat
Settings shape accessible via the bridge for not-yet-migrated consumers.
Parser and layering:
- ConfigLayer is now a newtype around v2 SettingsFile. Loading via
ConfigLayer::parse/load/settings/for_workflow/project now hard-fails
on legacy top-level keys (version, llm, vars, sandbox, etc.) with
targeted rename hints emitted by fabro_types::settings::v2::tree
- new fabro_config::merge module encodes the merge matrix directly:
replace-by-default maps, sticky merge for run.sandbox.env and
provider-native labels, splice-aware string arrays for
run.model.fallbacks and notification route events, whole-list
replacement for run.prepare.steps, field-merge keyed objects for
notifications/MCPs/web-auth providers, and ordered hook id-aware
replacement
- ConfigLayer::resolve delegates to fabro_types::settings::v2::bridge
so consumers keep reading through the legacy Settings shape until
Stage 4 migrates them off it
- effective_settings::resolve_settings now treats project/workflow/
run/features as shared layered domains and strips cli/server from
non-local layers before merging, fulfilling the owner-first trust
boundary rule
Consumer migration (Stage 4 preview, kept to the files that block
the workspace build):
- fabro-server run_manifest builds v2 RunLayer from ManifestArgs and
resolves manifest dockerfile references through the v2 sandbox
daytona snapshot tree
- fabro-cli manifest_builder consults run.goal via v2; user_config
writes the v2 server.storage.root field under the CLI storage-dir
override; run/overrides constructs a v2 RunLayer from RunArgs
- fabro-cli scaffolds (repo init, workflow create) emit _version = 1
with project.directory/workflow.graph/run.sandbox etc.
fabro-config / fabro-types legacy parse-time types (ProjectConfig,
LlmConfig, SandboxConfig, PullRequestConfig, ExecConfig, SettingsFile
try_into, etc.) are deleted from the parse path; the resolved type
re-exports (LlmSettings, SandboxSettings, etc.) remain as shims so
unmigrated consumers keep compiling.
fabro-test helper: settings.toml fixtures now use _version = 1 plus
[server.storage] root and [cli.target] type = "unix" path. Legacy
flat storage_dir/server.target handling removed from the sync path.
Known Stage 4/5 follow-ups:
- fabro-cli integration test fixtures still use legacy-shape TOML
(version = 1, [llm], [sandbox], [vars], [exec], [fabro], etc.);
tests currently fail to parse against the v2 schema as intended.
Migrating them is the bulk of Stage 4 and lands in subsequent
commits.
- OpenAPI ServerSettings schema, generated clients, apps/fabro-web
workflowData fallback, and docs/reference examples are unchanged
and land in Stage 5.
Stage captured artifacts in per-attempt tempdirs and persist them through an
explicit artifact sink instead of writing into run scratch cache.
Server-managed and test-owned runs now write directly to ArtifactStore, while
CLI worker runs keep the staged upload path. The local run summary now prints
durable artifact identifiers and copy hints rather than scratch-cache paths,
and the run-directory docs and integration coverage were updated to match.
7 IT tests in cmd/uninstall.rs covering:
- help snapshot
- not-installed detection (plain + JSON)
- dry-run preview without deleting
- --yes removes ~/.fabro/
- --json inventory output (dry-run + execute)
Also fixes the "not installed" check to use marker files
(settings.toml, certs/, storage/) instead of directory existence,
since the CLI's logging startup may auto-create the directory.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve new clippy failures introduced by the merge and update the root
help snapshot to include the uninstall command so fabro-cli lint and
test verification return to green.
Adds a top-level `fabro uninstall` command that reverses `fabro install`
and `install.sh`. Defaults to dry-run (preview) mode, requiring `--yes`
to execute.
Features:
- Inventory and dry-run preview with sizes and `--json` support
- Server shutdown (guarded — only when server is running)
- Safety guardrails (refuses to delete /, $HOME, or dirs without markers)
- Shell config cleanup (exact `# fabro` sentinel match, PATH validation,
atomic write via temp+rename)
- Binary status reporting with tailored brew/cargo/manual hints
- Exit code: 0 on success, 1 on critical failure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CommandContext to load machine settings once per invocation, cache
server access, and route migrated commands through the shared
ServerStoreClient path instead of reloading settings and reconnecting ad
hoc.
Add a server-side web.enabled toggle and CLI overrides so Fabro can run
with API and health only while disabling the embedded SPA, browser auth
routes, and web-only helper endpoints.
- fabro-types: remove redundant "freeform" match arm (match_same_arms)
- fabro-server: use let...else and remove needless return
- fabro-cli/runner: use while-let instead of match loop, unwrap Option
from build_artifact_uploader return type
- fabro-cli/attach: introduce AttachOptions struct to reduce bool
parameter count (fn_params_excessive_bools)
- fabro-test: fix unused variable and needless continue in session lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two flake sources identified across 100+ full-suite runs:
1. Session lock EINVAL race: cleanup_session_root's remove_dir_all
could delete the session root between with_session_lock's
create_dir_all and File::create, causing EINVAL. Fix: retry the
create-dir + create-file sequence as a unit.
2. mTLS cert generation: openssl req -key /dev/stdin failed under fd
pressure with "Bad file descriptor". Fix: read from the already-
written server.key file path instead of piping through /dev/stdin.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accept `--bind <ip>` as a TCP bind request while keeping the default
Unix socket behavior unchanged. Resolve host-only TCP binds inside the
serving process so startup output, server metadata, and status always
reflect the concrete host:port, preferring 32276 and falling back to a
random port with a warning when needed.
Move the built web bundle into an embedded fabro-spa crate so Cargo and
release builds no longer depend on Bun at build time, and preserve the
local dev override path for fast UI iteration.
At the same time, rename interview and agent-level aborted flows to
interrupted, keep cancelled for run-level shutdown, and stop reporting
skipped answers as interruptions in the run event stream.
Stabilize the recovery scenario around rebuilt metadata timing and node
ordinals, make in-process run cancellation converge on a cancelled
reason, and keep the label assertion unit test out of the shared
TestContext session lifecycle.
Replace the slow CLI integration tests that waited on worker shutdown
grace periods with focused coverage that still checks the important
behavior. The attach JSON test now finishes the gated run cleanly,
the rm force test uses a mocked server contract, and the Ctrl-C cancel
path is covered at the attach layer instead of through a full live run.
Add a cooperative subprocess cancel control message so cancel and delete
can abort pending interviews without relying only on the 5 second hard
kill fallback.
Replace bin-scoped localhost HTTP tests with command-facing integration
coverage so they run under the intended IT timeout budget without
changing nextest overrides.
Remove leftover object-backed terminology from the worker uploader,
rename the remaining scratch-fallback test to match current behavior,
and update the old artifact upload plan to reflect the current
no-fallback model.
Drop the dead artifact storage capability split from run records,
run.created events, and workflow/server create paths. Worker artifact
upload is now unconditional, and tests/snapshots no longer encode a
legacy object-backed distinction.
Persist submitted run manifests and accepted run definitions as SHA256
blob refs on run events, remove workflow_bundle.json from the runtime
path, and stop deleting shared CAS blobs when removing runs.
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.
Make ArtifactStore the only artifact read path, stop writing
manifest.json into run scratch, and update the CLI summary to
resolve artifact paths from the durable server API.
Stop writing scratch final.patch files now that diffs are projected from
run state, and remove the unused cache/artifacts/values plumbing while
keeping runtime/blobs materialization intact.
Update tests and run-directory docs to match the current scratch contract.
Drop scratch-only compatibility paths and legacy test scaffolding now that
SlateDB-backed state is authoritative. This removes scratch file fallbacks,
updates docs and UI labels, and moves tests onto durable store-backed helpers.
Guard server worker cleanup against superseded subprocesses so rewind and
resume flows do not append a synthetic failure from an older worker. Update
CLI snapshots for the current interview events and give the shared test
session lock more time to cover daemon startup and shutdown.
Collapse the live answer rendezvous into ControlInterviewer, move pending
question storage onto a shared typed record, and route HTTP and Slack answer
submission through one server-side flow.
Persist pending interviews in run state, deliver accepted answers to workers
through the server-owned control path, and remove the old scratch-file and
WebInterviewer transports.
This also moves Slack onto the canonical server answer flow, adds richer
question metadata to the API and run events, and covers the subprocess
question lifecycle with end-to-end tests.
Share one store-dump export pipeline across the server-backed CLI path
and the local test helper, and add mixed blob-ref plus artifact coverage
for the exported output.
Route `fabro store dump` through the server client for run state, events,
blob hydration, and artifact downloads instead of reopening storage directly
from the CLI process. This fixes blob-backed checkpoint exports and restores
store-dump coverage under the in-memory test server.
Reapply the lint-safe changes that were partially displaced while merging
origin/main, including the billing serialization assertion and the attach
replay/server annotation cleanups. This keeps the merged main branch back to a
clean full-workspace clippy pass before the store-dump debugging continues.
Tighten the worker upload path so object-backed runs only fail when an
artifact upload is actually attempted without a token, and update CLI
snapshots for the new artifact storage metadata.
Fold in the workspace test and clippy fixes needed to verify the final
artifact upload implementation cleanly across Rust and web targets.
Store large context payloads in the global CAS and keep durable
checkpoint state as blob://sha256 refs instead of execution-local file
paths. Resolve and materialize blob refs at execution, output, and export
time so resumed and remote runs can read legacy and new artifacts
consistently.
Add scoped worker upload tokens and HTTP artifact upload clients.
Support manifest-first multipart stage artifact uploads with validation and checksums.
Gate artifact reads by run capability while preserving legacy scratch fallback.
Active runs deleted through rm --force were removed from server state
without signalling the worker process, which could leave detached
workers orphaned after test cleanup. Terminate the tracked worker
process group before deleting run state and cover it with an
integration regression.