Mostly consolidation of code added in the recent schema v2 work:
- Share a single ActorRef::user() constructor between server control
actions and workflow provenance conversions.
- Share StageScope::from_context() between current_stage_scope and
StageScope::for_handler so the 4-field construction lives in one place.
- Collapse RunEvent::to_value's if-let chain into an insert_opt helper.
- Use Value::String(id.to_string()) instead of serde_json::to_value for
StageId/ParallelBranchId when seeding the parallel branch context.
- Share parse_event_envelopes via tests/it/support/mod.rs instead of
duplicating the parsing block in two CLI run_events helpers.
Also fix parallel-branch git.commit to emit via emit_scoped with a
branch-specific StageScope so it carries stage_id / parallel_group_id /
parallel_branch_id alongside the other stage-scoped events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hand-written to_wire_value / from_wire_value helpers
and the wire_event_envelope_from_generated bridge with
#[serde(flatten)] on EventEnvelope.payload. Derived serde now
produces and accepts the wire shape natively:
{ "seq": 42, "id": "...", "event": "...", ... }
instead of the nested { "seq": 42, "payload": { ... } } the
derive would otherwise emit. #[serde(flatten)] composes fine with
the #[serde(transparent)] EventPayload(Value) wrapper, so the
inner payload object is merged into the outer map on both sides.
- fabro-store/src/types.rs: add #[serde(flatten)]; delete the two
wire helpers (33 lines of Value-map poking); update the
round-trip test to assert the shape is actually flat.
- fabro-server/src/server.rs: sse_event_from_store serializes
the envelope directly; api_event_envelope_from_store pipelines
to_value into from_value.
- fabro-cli/src/server_client.rs: buffer_sse_events parses
straight into EventEnvelope via serde_json::from_str;
list_run_events uses the existing convert_type helper in place
of the deleted wire_event_envelope_from_generated bridge.
- fabro-cli tests: helpers that called from_wire_value now call
serde_json::from_value.
Drops the shape check that from_wire_value used to perform on
parse (id/ts/run_id/event must exist as strings): that check
extracted run_id from the payload and then validated it against
itself, so it only guaranteed presence, not correctness.
EventPayload::new(value, expected_run_id) still runs the same
check where a caller has a real external run_id to cross-match.
Generated code and the OpenAPI allOf(seq, RunEvent) schema are
untouched; the wire JSON is byte-identical before and after.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Centralize flattened EventEnvelope conversion in fabro-store so the CLI,
server, and test helpers reuse one wire-shape path. Also thread parallel
group and branch ids through nested stage and agent events so the new
envelope fields stay populated inside parallel branches.
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.
Replace the overlapping usage and cost model with canonical billing
primitives centered on ModelRef, ModelHandle, TokenCounts, and
BilledModelUsage. This also renames the public API and web surface from
usage to billing, removes compatibility aliases, and normalizes provider
usage adapters onto the shared billing vocabulary.
Default test daemons now opt into an in-memory object store and test
helpers carry explicit run ids instead of rediscovering runs from
shared state.
This also disables the disk-backed store dump integration tests until
store dump is routed through the server's live store handles.
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 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.
Add a shared StoredEvent schema in fabro-types and switch workflow,
store, CLI, and server event handling to use it directly.
This removes the writer/reader mismatch around flattened failure data,
updates affected projections and progress rendering, and refreshes the
fixture/snapshot coverage around the canonical event shape.
Merged origin/main incorporating:
- db_prefix threading in SlateRunStore for run isolation
- matches_run validation in active run cache
- NodeVisitRef type in fabro-store types
- ListRunsQuery parameter for list_runs API
- HashSet dedup in catalog listing
- Updated snapshot tests for new run directory format
Preserved from feature branch:
- NodeAsset struct and exports
- StageId-based node references in run state
- make_run_dir as pub for cross-crate access
- Thread-spawn approach in handler test_default for tokio safety
- parse_run_id handles YYYYMMDD-ULID directory format
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove API/CLI request and log artifact writes plus panic.txt now that
run state and events are store-backed. Update the direct tests to assert
returned behavior instead of on-disk debug files.
Replace in-process workflow E2E hook tests with fabro-cli workflow
integration tests that run fabro as a subprocess and pass OpenAI twin
env only to the child process. Remove the unsafe env-var mutation helper
from fabro-workflow integration tests.
- Move real_cli_claude/codex/gemini tests from fabro-workflow to fabro-cli,
which has a 20s nextest timeout (vs 6s default), and add poll_interval(10ms)
- Reduce DockerSandbox stop_container grace period from 5s to 1s
- Reduce timeout_handling test sleep from 60s to 2s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests were flaky because command() inherited the real repo as the
working directory. When the repo was clean, detached runs attempted
git worktree creation against it, sometimes failing and injecting
extra warning lines into snapshots.
Now command() defaults to the non-git temp_dir, eliminating this
class of flakiness. Tests needing a specific directory override with
.current_dir(). Also canonicalizes fixture paths and adds a
[FIXTURES] snapshot filter via test_context!() macro.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the 6 parametrized workflow scenarios from scenario/workflows.rs
into a new workflow/ directory with one file per test. Move fixture
.fabro files from test/scenario/ to workflow/fixtures/ co-located with
the tests.
Rename the scenario_tests! macro to sandbox_tests! in the new module
for clarity. Slim scenario/ down to just lifecycle and exec tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>