Use HTTP health checks with short deadlines for managed server readiness and add finite control-plane request timeouts for CLI/server clients. Keep stream bodies uncapped so SSE attach flows can remain long-lived.
Drop a resync useEffect that healed activeIndex back to safeIndex —
safeIndex already clamped reads, so the effect only triggered an
extra render. Reuse the shared ErrorMessage from ui.tsx instead of
the inline copy. Drop a useMemo over a tiny per-render array.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the read-only BlockedRunNotice with a viewport-fixed dock that
lets users answer pending human-in-the-loop questions without dropping
to the CLI. Supports YesNo, Confirmation, MultipleChoice, MultiSelect,
and Freeform question types, plus the allow_freeform fallback for
choice-with-write-in. Multiple pending questions surface a "+N more"
pill so a parallel-handler run can be drained from one place.
The dock subscribes to interview.* SSE events for auto-refresh and
posts answers via the existing /runs/{id}/questions/{qid}/answer
endpoint. Cancel is consolidated into the page header (now shown for
blocked runs) so the dock chrome stays focused on the conversation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Leftover from when rules.rs was a single 3500-line file. Numbering
was stale (Rule 23 and Rule 24 each appeared twice after the split)
and duplicated info already in the filename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep fabro_workflow::event as the public facade while moving event conversion, names, redaction, sink, emitter, stored-field helpers, and StageScope into focused modules. Co-locate the existing event tests with the moved code and update the events strategy docs for the new module layout.
Tighten auth state to remove impossible identity branches and stringly error codes.
Route worker JWTs by header metadata, avoid unnecessary auth context cloning, and reuse shared helpers across tests and Slack payload handling.
Carry typed timeout actor metadata through failures instead of deriving it from display text.
Replace hand-built RequestAuthContext literals in github_webhook with the
existing ::invalid()/::authenticated() constructors, collapse the duplicate
demo/real principal layers into a single cloneable layer, and forward the
_with_anyhow error constructors to their _with_source twins to drop the
duplicated cause-collection bodies. refresh_credential_from_headers now
reuses jwt_auth::bearer_token_from_headers for Authorization parsing.
test_support shares one TEST_DEV_TOKEN-derived bearer header instead of a
hand-pasted literal. Replace for-loops in principal/cli_flow tests with
per-variant cases to honor the no-loops-in-tests rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract run_artifact_entry_from / artifact_entry_from in fabro-server so
the two list-artifact handlers share a single conversion site. Push the
?retry=... query append into stage_artifacts_url in fabro-client so
upload callers don't repeat it. Replace required_filename and
required_retry with one generic required_query_param<T> helper.
(From impls were the cleaner shape but the orphan rule blocks them:
NodeArtifact lives in fabro-store, RunArtifactEntry in fabro-api,
neither is in fabro-server. Free fns achieve the same dedup without
adding a fabro-store -> fabro-api coupling.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defer current_visit_for to the fallback branch in stage_entry_with_current_visit
so events that already carry stage_id avoid an O(N stages) scan per event.
Run state() and list_events() concurrently in build_conclusion_from_store, and
share a single retry- segment prefix between encode and decode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
provider_used, script_invocation, and script_timing become object | null;
parallel_results becomes Array<object> | null. The Rust StageState type is
unaffected because fabro-api/build.rs replaces it with fabro_types::StageState.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract STAGE_RANK_WIDTH and a derived MAX_STAGES_IN_DUMP in fabro-dump so
the path-prefix format and the stage-count cap can't drift, and replace the
two `{rank:03}-...` literals with a shared stage_dir_name helper.
Replace the cli/dump.rs `u32::try_from(artifact.retry)` with the symmetric
inverse of the server's `cast_signed()` emit. The OpenAPI schema declares
`minimum: 0`, so the negative branch is unreachable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove unused RunProjection::stage_mut, share decode_retry_and_filename
between artifact_store decoders, reuse stage_visit() in the artifact
lifecycle, and cache the dump.log entry index so RunDump::add_orphan_notice
no longer rescans entries on every call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eight reducer arms repeated the same node_id-presence check followed by a
visit-derivation step (either explicit from props, or
`current_visit_for(...).unwrap_or(1)`) and a `stage_entry` call. Pull
those into `stage_at_visit` and `stage_at_current_visit` so each arm just
binds the projection entry and writes its fields. The visit-derivation
strategy is now legible from the helper name instead of buried in a
free-floating `let visit = ...` line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expose `fabro_types::first_event_seq` next to `StageProjection`, replacing
six identical `nonzero` test helpers and the private one in `run_state`.
Add `RunProjection::iter_stages_mut` so `SerializableProjection` can
clear bulky fields without the collect-then-lookup dance, and let the
`fabro-dump` loop iterate `(&StageId, &StageProjection)` borrows directly
to drop the per-stage `StageId::clone()` and redundant HashMap lookup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to the workspace-wide error chain preservation: removes the
`From<String>` impl on `PullRequestApiError`, the unused `SharedError::as_anyhow`,
and the test-only `DisplayContains`/`DisplayStringExt` traits that papered over
String errors. Call sites now build `anyhow!` errors directly and tests stringify
errors explicitly via `.to_string()`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep typed transport and provider errors intact through API, GitHub, OAuth, install, diagnostics, and artifact paths. Add regression coverage for cloned shared errors and communication error chains.
Restore snapshot form for auth status JSON test so re-introducing an
env_dev_token field would fail the snapshot, and rename the ps test to
reflect that it proves FABRO_DEV_TOKEN is ignored rather than that auth
is generally required.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Short-circuit the controlled shell wrapper when the stop file already exists so a cancelled Docker exec does not launch user code before the pid watcher can terminate it.
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>
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>
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>
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>
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>