`inherit = false` clears HOME but `dirs::home_dir()` falls back to the
passwd database, picking up the runner's ~/.fabro/cli.toml. The loaded
app_id without GITHUB_APP_PRIVATE_KEY triggers a partial-config error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Emit reason (condition/preferred_label/suggested_next/unconditional/
jump/fallback), stage_status, preferred_label, suggested_next_ids,
and is_jump so logs explain why an edge was chosen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The doctor dry-run test was failing in CI because it inherited the host
environment. With no LLM API keys set, the doctor reported errors and
exited non-zero. Adding `inherit = false` to all 18 .toml test files
ensures deterministic behavior regardless of the host environment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Markdown was rendered at full terminal width then indented, pushing lines
past the right edge. Now wraps to terminal_width minus indent first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When running `fabro run smoke`, the slug "smoke" was used to locate the
workflow but never persisted. If the DOT graph name diverged from the
directory name (e.g. workflows/foo/ contains digraph Bar), resolve_run
couldn't find the run by slug. Now the slug is extracted from the
workflow path, stored in the manifest, and matched in resolve_run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Workflow names in manifests are PascalCase (e.g. "LegacyTool") but
users expect to type the slug (e.g. "legacy-tool"). resolve_run now
compares case-insensitively and with hyphens/underscores stripped,
so both forms work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supports raw JSONL output (pipeable to jq) and --pretty mode with
colored, formatted output showing stages, tool calls, and assistant
messages. Includes --follow, --since, and --tail filtering options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Subcommands like cp, diff, preview, ssh, and pr previously only
accepted run ID prefixes. The new resolve_run() tries run ID prefix
first, then falls back to workflow name (most recent run), making
these commands more ergonomic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`sh --version` exits non-zero on dash (Ubuntu default), so the test
only passed on macOS where sh is bash. Use `git` instead which
reliably supports --version on all platforms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The JSONL listener's run_id was initialized to "" and only populated
when WorkflowRunStarted fired, but sandbox events emit before that.
Seed it with the already-generated ULID so all events carry the run_id.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Writes a starter workflow.fabro (DOT graph) and workflow.toml into the
project's workflows directory. Supports --goal flag and derives the
digraph name from the workflow name using PascalCase conversion. Also
defaults the `graph` field in workflow.toml to "workflow.fabro" so it
can be omitted from generated configs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The inline merge in run_command() duplicated the structure of
apply_defaults() with shallower (inconsistent) semantics. This extracts
a proper merge_overlay method that deep-merges compound fields (vars,
hooks, mcp_servers, sandbox sub-fields) consistently with apply_defaults.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add project-level run defaults to fabro.toml so sandbox, LLM, hooks,
MCP servers, and other settings can be shared across workflows instead
of duplicated in each workflow.toml. Precedence: workflow.toml >
fabro.toml > cli.toml/server.toml.
- Rename `directory` → `work_dir` with backwards-compat serde alias
- Add `hooks` and `mcp_servers` to `RunDefaults` with merge logic
- Extend `ProjectConfig` with all run-defaults fields + `into_run_defaults()`
- Remove duplicate `McpServerEntry` from fabro-config (use run_config's)
- Move `hook_config` from ServerConfig into `run_defaults.hooks`
- Wire project config merge and hooks/mcp fallbacks in run_command()
- Update OpenAPI spec and regenerate TypeScript client
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an @file reference can't be resolved against the workflow's
directory, fall back to ~/.fabro/ so users can share prompt files
across workflows without duplication. The workflow directory keeps
higher precedence so project-specific overrides still win.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete the rebrand by replacing hardcoded "arc/run/" branch prefixes
with a RUN_BRANCH_PREFIX constant ("fabro/run/") and updating
fabro init to create workflows under fabro/workflows/ instead of
arc/workflows/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bare ubuntu:22.04 image lacks git and other tooling, causing git
checkpoints to fail. Switch to the daytona-medium snapshot which has
standard dev tools pre-installed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The engine writes git_commit_sha to on-disk checkpoint.json but not to the
metadata branch. When the metadata checkpoint lacks this field, walk the
run branch and match commits by message pattern to fill in the SHAs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enables rewinding both the metadata branch and run branch refs to a
target checkpoint, allowing resume from an earlier point with
`fabro run --run-branch`. Supports targeting by node name, node@visit,
or @ordinal, with parallel interior snap-back and optional remote push.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Show workflows grouped by User/Project with directory paths in headings,
aligned NAME/DESCRIPTION columns, truncated goal snippets, and (none)
for empty sections. Add tests for list_workflows_detailed, read_workflow_goal,
and truncate_str.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a new `workflow` subcommand group with a `list` command that
discovers available workflows via `fabro.toml` and prints their names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`fabro run NAME` now checks ~/.fabro/workflows/ as a fallback when the
workflow isn't found in the project directory, letting users have
personal workflows available across all projects. Project workflows
take precedence over user workflows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update 47 MDX doc pages, OpenAPI spec, SVG diagram, language
grammar, frontend demo data, marketing page, skills, and README
to use .fabro extension. Add "fabro" to fileTypes in language
grammars. Document stack.child_workflow alongside stack.child_dotfile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename 79 workflow files from .dot to .fabro extension across
fabro/workflows/, test/, test/docs/, and files-internal/demo/.
Update TOML configs, Rust production code, test code, and shell
scripts. Backward compat tests in test/attractor/ are unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Write graph.fabro in run dirs and metadata branches. Read with
graph.dot fallback for backward compatibility with existing runs.
Add stack.child_workflow attribute with stack.child_dotfile fallback.
No files renamed yet — fallback paths handle everything.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Warn when thread_id is set without fidelity=full, since session reuse
only works with full fidelity. Checks node-level, edge-level, and
graph-level default_thread attributes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The path was two levels up (../../) but needs three (../../../) since
the package lives at lib/packages/fabro-api-client.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split the single cli_tests trycmd function into 14 per-subcommand test
functions so nextest can parallelize them. Delete header.toml and
verbose.toml doctor fixtures that ran live network probes — the existing
dry-run-flag.toml and help.trycmd already cover the same output assertion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- doctor_no_color test: add --dry-run flag to skip live network probes (11s -> <1s)
- retry tests: set retry_policy=linear for 500ms delay instead of 5s default backoff
- cli_backend tests: make poll_interval configurable on AgentCliBackend, use 10ms in tests
- doctor TOML fixtures: remove status.code=1 now that dry-run doctor exits 0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fabro-util is a low-level crate depended on by almost everything, so its
build.rs reruns cascaded through the whole workspace. fabro-cli is the
leaf binary — nothing depends on it, so rebuilds are limited to just
that one crate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The relative path to .git/HEAD was wrong (../../ instead of ../../../),
pointing to a nonexistent file. Cargo reruns build scripts when a
rerun-if-changed target doesn't exist, causing a full workspace rebuild
on every invocation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The assemble_pr_body function unconditionally appends the "Generated
with Fabro" footer, but this test was asserting the bare narrative.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a human gate has only a freeform edge and no fixed choices, use
QuestionType::Freeform instead of MultipleChoice. This sends the user
directly to a text input prompt, skipping the unnecessary select menu.
Also removes the Quit choice from the REPL workflow so it uses the new
freeform-only path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Thread the previous stage's response through `Question.context_display`
so `ConsoleInterviewer` renders it before the interactive prompt. This
lets users see the agent's response before deciding what to do next in
looping workflows like REPL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update default git author to Fabro <noreply@fabro.sh>. Append
"Generated with Fabro" footer to all checkpoint and metadata commits,
and to PR bodies. When the commit author is overridden, add a
Co-Authored-By trailer for attribution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename `llm_model` → `model` and `llm_provider` → `provider` in stylesheet
properties, accessor methods, and all DOT/doc references. Add
ProviderInferenceTransform that automatically infers provider from the model
catalog, eliminating redundant provider declarations in stylesheets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Box the Usage field in Turn::Assistant to satisfy clippy::large_enum_variant.
Rename ARC_ prefixed env vars to FABRO_ in .env.example.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allows users to specify a reachable base URL for SSH hosts so that
get_preview_url(port) returns the correct external URL instead of
falling back to localhost.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace docs/images screenshots (runs-board, run-detail, workflow-example)
with fresh captures from the current FABRO-branded UI in demo mode.
Also fix a panic in arc-api demo::run_config_to_api where None-valued
HashMap fields (e.g. sandbox.env) serialized to JSON null, which failed
to deserialize into non-optional HashMap fields in RunConfiguration.
Added strip_nulls helper to clean the intermediate JSON value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `client: reqwest::Client` field (matches LinearTracker pattern); eliminates 7 per-call client constructions that prevented connection pool reuse
- Remove `graphql_url: String` field — always derivable from base_url, computed via `graphql_url()` method at each call site
- Extract `make_test_issue()` test helper to replace 3 copy-pasted Issue struct literals
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New arc-tracker crate defines the Tracker trait and shared Issue/BlockerRef types
- arc-linear: replace Issue/BlockerRef with arc-tracker re-exports, add LinearTracker struct implementing Tracker, remove free functions
- arc-github: add GitHubTracker implementing Tracker via Projects V2 GraphQL API, with lazy project node ID resolution (org → user fallback) and create_installation_access_token_for_projects helper
- 47 tests in arc-github (15 new), 24 tests in arc-linear (updated to use LinearTracker)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Make execute_graphql private (callers use domain functions)
- Extract extract_issues() helper to deduplicate node-parsing blocks
- Replace O(n^2) .find() loop with HashMap for ID-ordered results
- Add tracing::debug on all public functions, warn on HTTP errors
- Extract BLOCKS_RELATION_TYPE constant for magic string
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>