Commit graph

358 commits

Author SHA1 Message Date
Bryan Helmkamp
ab4d299fde
refactor(workflow): compute snapshot stats inside the blocking task
write_snapshot_blocking now derives entry_count and bytes from the
entries slice instead of taking them as parameters. The arity drops
from five to three, and the cheap O(n) work moves off the async
runtime into spawn_blocking where the rest of the snapshot already
runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 10:21:38 -04:00
Bryan Helmkamp
964c31837e
refactor(workflow): reuse GitHubCredentials::resolve_bearer_token
Make GitHubCredentials::resolve_bearer_token public and call it from
run_metadata::mint_token instead of re-implementing the JWT-sign +
installation-token branch. Eliminates the unreachable!() that arose from
matching the same enum twice.

Also drop the metadata_ field-name prefix on RunMetadataRuntime fields
(degraded, warning_emitted) — the prefix is redundant inside a struct
already named RunMetadataRuntime. Method names unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 10:12:31 -04:00
Bryan Helmkamp
84ab48d534
Merge origin/main into git2-metadata-writer branch
Origin advanced 11 commits in parallel, including refactors that
restructured the now-deleted sandbox_metadata fast-import writer
(structured ExecFailure for push errors, redacted_output_tail helper,
RunDump moved to fabro-dump crate, RunDump::from_projection now returns
Result, MetadataSnapshotFailureKind::Write, MetadataSnapshotFailed event
gains exec_output_tail, RunStoreBackend gains read_run_log).

Resolution: take ours for the four metadata-writer files (sandbox_metadata
deleted, lifecycle/git.rs, pipeline/finalize.rs, sandbox_git.rs) since the
git2 writer supersedes that module. Fold origin's API changes into the
ours-side: switch to fabro_dump::RunDump, handle from_projection's Result,
populate exec_output_tail: None in MetadataSnapshotFailed (git2 push
failures have no exec stdout/stderr), implement read_run_log on test
mocks. Drop unused from_raw_entries from fabro-dump.

A follow-up will port the structured push-failure pattern to run_metadata
without widening ExecFailure to non-exec ops.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 10:00:27 -04:00
Bryan Helmkamp
155d0a5a28
Merge remote-tracking branch 'origin/main' 2026-05-01 09:43:39 -04:00
Bryan Helmkamp
84a6ee941a
fix(workflow): adopt MetadataSnapshotFailure in dump-error branches
Adapt the from_projection error branches in lifecycle/git.rs (init + checkpoint phases) and pipeline/finalize.rs to the new free-function emit_metadata_snapshot_failed and MetadataSnapshotFailure struct introduced in 543725752. The merge auto-resolved cleanly but left the dump-error sites on the deprecated method/positional-args signature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:42:16 -04:00
Bryan Helmkamp
8a1828cc95
refactor(workflow): reuse fabro-checkpoint Store and dedupe helpers
Replace the hand-rolled tree builder in run_metadata with
fabro-checkpoint's Store::write_blob/write_tree/write_commit/update_ref,
deleting BuildTreeError, TreeNode, build_tree, insert_tree_node, and
write_tree_node. Also fold three smaller duplications: the identical
metadata_writer_for_repo test helpers in lifecycle/git.rs and
pipeline/finalize.rs become RunMetadataWriterHandle::new_for_test_repo,
sandbox_git_runtime reuses sandbox_git::exec_err, and METADATA_PERMISSIONS
is a LazyLock instead of being rebuilt per snapshot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:39:18 -04:00
Bryan Helmkamp
5ef4b87878
Merge remote-tracking branch 'origin/main' 2026-05-01 09:34:44 -04:00
Bryan Helmkamp
2ef87240e7
fix(dump): propagate serde errors from RunDump::from_projection
push_json_entry and push_json_entry_path silently dropped entries via if let Ok(...) on serde_json::to_value, hiding any future Serialize impl failure as missing files. They now return Result, RunDump::from_projection returns Result<Self>, and the three production callers (pipeline/finalize, lifecycle/git init + checkpoint) report failures via emit_metadata_snapshot_failed with MetadataSnapshotFailureKind::Write.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:32:48 -04:00
Bryan Helmkamp
a4cfc617aa
refactor: expose redacted_output_tail as a public free fn
Drops the synthetic ExecResult fabricated in sandbox_metadata::stdout_output_tail
just to reach private redaction logic. The redact + sanitize + tail pipeline
now lives behind fabro_sandbox::redacted_output_tail(stdout, stderr, max),
which ExecResult::redacted_output_tail also delegates to.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:21:59 -04:00
Bryan Helmkamp
5437257521
refactor: unify metadata snapshot failure helpers and drop floor_char_boundary copies
Groups the MetadataSnapshotFailed event payload into a MetadataSnapshotFailure
struct and replaces the two near-identical 11-arg emit_metadata_snapshot_failed
helpers in lifecycle/git.rs and pipeline/finalize.rs with one shared helper
in sandbox_metadata.rs. Both #[allow(too_many_arguments)] blocks are removed.

Also deletes two hand-written floor_char_boundary copies (fabro-agent and
fabro-sandbox) in favor of the stable str::floor_char_boundary, matching how
most existing call sites already use it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:17:47 -04:00
Bryan Helmkamp
7f23038820
refactor(retro): drop redundant mkdir and dead run_dir plumbing
Sandbox::write_file already creates parent dirs in every backend (local, docker, daytona), so the per-file mkdir -p exec_command in upload_data_files was a wasted round-trip. Also remove the run_dir param/field that became unused after RunDump took over hydration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:49:02 -04:00
Bryan Helmkamp
96d7ba4aa0
refactor: dedup exec failure diagnostic plumbing
Collapses four duplicate tracing field blocks in event.rs behind
ExecOutputTail::trace_summary, drops the parallel MetadataPushError
struct in favor of reusing SandboxMetadataError::Operation, inlines
the single-use Error::exec_result accessor, and gates the test-only
ExecResult::from_process_output to cfg(test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:46:08 -04:00
Bryan Helmkamp
2f7aeba417
fix(workflow): preserve exec failure diagnostics
Add bounded redacted exec output tails to failure events while keeping tracing log-safe. Centralize tail projection on ExecResult and thread diagnostics through metadata, setup, devcontainer, and CLI install failures.
2026-05-01 08:45:50 -04:00
Bryan Helmkamp
165b38c3ed
refactor(workflow): write metadata snapshots with git2
Replace the sandbox-side fast-import metadata writer with an in-process git2 writer that builds metadata commits locally and pushes them with worker-side GitHub credentials. Keep sandbox git probing separate from metadata runtime state so checkpoint commits and metadata snapshots have independent lifecycles.
2026-05-01 00:22:01 -04:00
Bryan Helmkamp
93908de46c
refactor(retro): share run dump hydration
Move RunDump into fabro-dump so CLI export and retro uploads share the same hydrated run layout. Drop the legacy artifact file-ref parser, add best-effort run.log retrieval for retro, and update retro prompts/docs to use events.jsonl and checkpoints.
2026-04-30 23:31:12 -04:00
Bryan Helmkamp
7cb120b96c
refactor: drop type assertions and reuse generated enums
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
Replace local CommandTermination/CommandOutputStream literal unions with
the generated enums from fabro-api-client, drop `as` casts and the `id!`
non-null assertion in run-stages, flatten the 6-deep status ternary into
streamStatus(), and use fabro_util::time::elapsed_ms in handler/llm/cli.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 22:45:38 -04:00
Bryan Helmkamp
de64136a0e
refactor: tighten command-log streaming hot paths 2026-04-30 22:45:38 -04:00
Bryan Helmkamp
3827d3946e
fix(daytona): satisfy streaming smoke lints 2026-04-30 22:45:38 -04:00
Bryan Helmkamp
d6b9e9553b
test(daytona): assert timeout termination state
Tighten the live Daytona timeout coverage so it proves timeout is represented as a missing exit code with the timed_out termination state, not just any non-success result.
2026-04-30 22:45:37 -04:00
Bryan Helmkamp
e50df2b58b
feat(command): distinguish cancelled commands from timeouts
Represent command termination explicitly across sandbox results, events,
run projections, API types, and the run stage UI. This removes the fake
-1 exit code path for timeout/cancel and lets consumers tell cancelled
commands apart from timed-out commands.
2026-04-30 22:45:37 -04:00
Bryan Helmkamp
c5c98bfbf2
fix(command): preserve diagnostics on early failures
Include captured stdout and stderr tails in timeout handler errors, and discard pre-created scratch logs when command spawn fails before any output can be finalized.
2026-04-30 22:45:37 -04:00
Bryan Helmkamp
8ac400df1c
feat(command): stream command logs from CAS-backed storage
Persist command stdout/stderr through scratch logs and finalized CAS refs, expose byte-offset tailing through the API, and render separate streaming panels in the web run view.

Resolve command output blob refs for execution-time consumers such as edge routing and retros, and make Docker streaming timeout/cancel drain output before returning.
2026-04-30 22:45:37 -04:00
Bryan Helmkamp
f18da11854
chore(logs): promote run lifecycle traces to info level
Promotes per-run observability events (stage start/complete, edge
selection, checkpoint, fidelity resolution, agent session, LLM stream
finish, tool calls, sandbox cleanup, PR build/create) from debug to
info so default-level operators see end-to-end run progress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:25:37 -04:00
Bryan Helmkamp
d65c1fa635
refactor(types): remove stage status compatibility 2026-04-30 06:48:47 -04:00
Bryan Helmkamp
f16391485b
refactor(workflow): update stage outcome semantics 2026-04-30 06:06:51 -04:00
Bryan Helmkamp
b12893eee8
refactor(workflow): encode retry as failed outcome intent 2026-04-30 06:00:04 -04:00
Bryan Helmkamp
71ec9fdee6
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-types/src/lib.rs
2026-04-29 20:43:12 -04:00
Bryan Helmkamp
25cd80c072
refactor(api): unify leaf API types 2026-04-29 20:21:23 -04:00
Bryan Helmkamp
07d3890bb8
refactor(workflow): share metadata snapshot helpers
Centralize compatibility notice detection and elapsed-time/error-cause helpers used by metadata snapshot event rendering and emission.
2026-04-29 19:43:30 -04:00
Bryan Helmkamp
4a4f0f8548
feat(workflow): emit metadata snapshot events
Add typed metadata snapshot events around init, checkpoint, and finalize archive writes so run logs expose durable metadata timing and failures. Include snapshot accounting, CLI rendering with compatibility-notice suppression, and event documentation.
2026-04-29 19:31:13 -04:00
Bryan Helmkamp
ece80c750d
Merge pull request #176 from justinabrahms/fix/at-path-resolves-relative-to-workflow-file
fix: resolve @path references relative to workflow file, not CWD
2026-04-29 09:57:33 -07:00
Bryan Helmkamp
32166e4cdf
refactor(sandbox): simplify exec error helpers and metadata snapshot
Inline static credential-refresh failure tags instead of round-tripping
through a classifier whose substring matches always returned the
sentinel its callers prepended. Drop the dead `Error::Exec` accessors
in favor of pattern matching, and replace the redundant
`MetadataSnapshot::pushed` field with `push_error.is_none()`. Also fix
a regression in Docker `refresh_push_credentials` that was discarding
stderr and exit code on `set_url_nonzero` failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:52:30 -04:00
Bryan Helmkamp
cebdf8dee5
Merge remote-tracking branch 'origin/main' into pr-176
# Conflicts:
#	lib/crates/fabro-cli/src/manifest_builder.rs
#	lib/crates/fabro-server/src/run_manifest.rs
2026-04-29 12:45:33 -04:00
Bryan Helmkamp
067fa3ee82
fix(sandbox): make git push failures log-safe
Add structured exec errors whose Display output keeps raw command output out of logs and notices while preserving stdout/stderr through explicit accessors. Stop Daytona from logging raw command strings and propagate git_push_ref errors so metadata push warnings include safe failure detail.
2026-04-29 12:40:50 -04:00
Bryan Helmkamp
91a0bdc3f4
refactor(manifest): tighten ManifestPath API
- Add `parent_or_dot()` helper to replace the repeated
  `.parent().unwrap_or_else(|| Path::new("."))` idiom at three call sites.
- Add `From<ManifestPath> for PathBuf` and use it in
  `BundleFileResolver::resolve` to drop a per-resolve `PathBuf` clone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:37:46 -04:00
Bryan Helmkamp
786e01c7a7
refactor(manifest): type bundle paths end-to-end
Introduce ManifestPath as the canonical in-memory key for run manifests so CLI-produced bundle keys and workflow/server consumers share the same normalization rules. Validate wire keys at the server boundary and add a CLI-to-server round-trip test for user-global @path references.
2026-04-29 12:26:39 -04:00
Bryan Helmkamp
6cb185b858
feat(github): point install errors at the configured app and require creds for docker
GitHubAppCredentials now carries the configured app slug, so the "not
installed" error from the installation lookup links to the specific
app's install page (https://github.com/organizations/{owner}/settings/apps/{slug}/installations)
when known, instead of the generic org installations page. Threaded
through the server, workflow pipeline, and CLI runner.

Also treat docker like daytona for GitHub credential gating: both are
clone-based providers that need an installation token to fetch the repo,
so a docker run now requires credentials when daytona would.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 10:43:00 -04:00
Justin Abrahms
c93c52e131
style: fix rustfmt nightly formatting in test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 08:14:01 +02:00
Bryan Helmkamp
e3c7dbb88d
perf(workflow): batch metadata snapshots with fast-import
Replace per-file sandbox metadata git writes with one fast-import stream per metadata commit while preserving the push-after-each-commit contract. Cover binary files, quoted paths, parent linkage, and per-snapshot push behavior in the metadata writer regression test.
2026-04-28 15:41:06 -07:00
Bryan Helmkamp
2cbd3768b8
fix(cli): prevent manifest git push prompts
Disable terminal prompts for manifest pre-run push attempts and cover the behavior with an offline remote-helper regression test.
2026-04-28 14:36:27 -07:00
Bryan Helmkamp
642e312cca
refactor: unify duplicate API types via with_replacement
Eliminate four parallel-type duplications between fabro-api generated
DTOs and fabro-types canonical types. The wire shape is owned by
OpenAPI; canonical types are reused via fabro-api/build.rs
with_replacement so the adapter functions and silent unwrap_or_default
defaults disappear.

- SecretType moves to fabro-types (was fabro-vault); deletes
  secret_type_from_api adapter.
- DiffLineStats renamed to DiffStats, moved to fabro-types, switched
  u64 -> i64 to match the OpenAPI integer; deletes line_stats_to_api.
- ManifestPreRunPushOutcome rewritten as a oneOf+discriminator
  PreRunPushOutcome over five variant schemas, deleting both
  pre_run_push_outcome_from_manifest and build_manifest_push_outcome.
- ManifestGit and PreRunGitContext unify as GitContext: dirty:
  DirtyStatus replaces clean: bool (preserving the Unknown state
  previously truncated on the wire), sha becomes Option<String>, and
  origin_url/branch fold into the unified context. RunSpec and
  RunCreatedProps flatten three fields (repo_origin_url, base_branch,
  pre_run_git) into a single git: Option<GitContext>.

Each replacement gets a fabro-api parity test (TypeId equality plus
JSON roundtrip) modeled on run_summary_round_trip.rs. TS client
regenerated.

Greenfield app, no production deployments — wire contract changed
directly without backwards-compat shims.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:29:37 -07:00
Bryan Helmkamp
20e161bc07
refactor: rename checkpoints_disabled -> in_place
The persisted bool described user intent (\"the user opted into the
in-place execution mode\"), not a literal consequence -- SlateDB and
event-sourced checkpoints flow regardless of the flag, only git
checkpoints are skipped. Renaming aligns the name with intent and
decouples it from any future implementation that allows git
checkpoints in-place.

The fork validator still consults this bool to bail out with a clear
error before searching for git checkpoints that won't exist.
2026-04-28 09:33:59 -07:00
Bryan Helmkamp
928b2f585b
refactor: drop WorkdirStrategy and RunOptions.checkpoints_disabled
WorkdirStrategy was structurally redundant with the existing
LocalSandboxLayer.worktree_mode config — Local sandboxes always picked
LocalWorktree, everything else picked Cloud, and the LocalDirectory arm
was only ever reachable via the parallel checkpoints_disabled bool.

resolve_worktree_plan now reads worktree_mode directly: Cloud sandboxes
return None with a pre_run_git base sha; Local + Never returns None
with no base sha; Local + non-Never builds the WorktreePlan as before.

RunOptions.checkpoints_disabled drops out: the lifecycle gate becomes
has_run_branch (git: None alone is the canonical "no git checkpoints"
signal), and tests/fixtures stop carrying the field.
2026-04-28 09:24:41 -07:00
Bryan Helmkamp
b24c67acef
fix: handle non-git local sandbox runs
Skip worktree checkpoint setup when a local sandbox is not backed by a git repository, and keep the API contract aligned with RunSpec serialization for omitted labels.
2026-04-28 08:22:58 -07:00
Bryan Helmkamp
ab9b28875b
fix: close sandbox-native metadata gaps
Ensure local runs use the worktree checkpoint path by default, expose source and sandbox paths in API/web surfaces, and remove dead fork/rewind push controls. Update docs for clone-based sandboxes and durable checkpoint timelines.
2026-04-28 08:05:18 -07:00
Bryan Helmkamp
80aad30f73
fix: close sandbox-native git metadata gaps
Add shared sandbox git validation for checkpoint paths, preserve forked run projection state, and record CLI remote mismatches explicitly. Refresh the API/client docs for durable run-store timeline and structured run specs.
2026-04-28 07:31:01 -07:00
Bryan Helmkamp
0543c5c8fe
chore: simplify sandbox-native git metadata code
- Reuse fabro_sandbox::shell_quote in sandbox_metadata.rs and sandbox_git.rs
  (CLAUDE.md mandates the shared helper, not local reimplementations).
- Skip git_diff call on first checkpoint when prev SHA equals new SHA;
  previously diffed a SHA against itself, costing one sandbox round-trip.
- Drop tuple-match theatre in write_snapshot cleanup.
- Type LEVEL_COLOR as Record<LogLevel, string> so the lookup is exhaustive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 06:39:01 -07:00
Justin Abrahms
c7851f4e08
fix: resolve @path references relative to workflow file, not CWD
`normalize_logical_path()` silently dropped leading `..` components
because `PathBuf::pop()` on an empty buffer is a no-op. For user-global
workflows (~/.fabro/workflows/) invoked from an unrelated CWD, the
manifest builder produces logical paths with leading `..` segments, but
the BundleFileResolver normalized them differently during lookup —
stripping the `..` — causing a key mismatch and leaving `@` references
unresolved.

Preserve `..` when there is no normal component to collapse.

Closes #175

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 09:33:13 +02:00
Bryan Helmkamp
cdd46b4fa8
Make git metadata sandbox-native 2026-04-27 21:43:15 -07:00
Bryan Helmkamp
af56b38e55
fix(lints): resolve clippy errors after error-chain refactor
Update test sites to call .to_string() before .contains() since the
sandbox Error enum no longer dereferences to String, add use statements
to satisfy clippy::absolute_paths, and inline the redundant
sandbox_error helpers in fabro-agent to clear needless_pass_by_value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 11:37:09 -07:00