Commit graph

844 commits

Author SHA1 Message Date
Bryan Helmkamp
9b0b8d94fc
fix(api): generate typescript EventEnvelope with typed seq
The typescript-axios generator was collapsing EventEnvelope's
allOf([inline_object, $ref: RunEvent]) to a bare `type
EventEnvelope = RunEvent` alias, losing the `seq` field at the
type level. TypeScript consumers could write `envelope.seq` and
get `any` (via RunEvent's additionalProperties index signature),
but had no type-level guarantee that seq was present.

Extract `EventSeq` as a named component schema and switch
EventEnvelope's allOf to two $refs. typescript-axios now
generates `export type EventEnvelope = EventSeq & RunEvent`,
which makes `envelope.seq: number` a typed property.

The Rust progenitor client is unchanged: it still flattens the
allOf into a single EventEnvelope struct with `seq: i64` inline,
exactly as before. Wire JSON is byte-identical on both sides.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 16:00:05 -04:00
Bryan Helmkamp
30fcee98c2
refactor(events): type stage/parallel ids with newtypes
Promote RunEvent.stage_id / parallel_group_id / parallel_branch_id
and the internal Event enum's matching fields from stringly-typed
Option<String> to Option<StageId> / Option<ParallelBranchId>. The
wire contract is now self-enforcing: malformed strings are rejected
at the serde seam, not quietly round-tripped, and the three
StageId::new(...).to_string() calls in stored_event_fields() just
drop the .to_string() since the newtypes flow straight through.

- fabro-types/src/stage_id.rs: new ParallelBranchId { group: StageId,
  index: u32 } mirroring StageId's Display / FromStr / serde string
  form. "{group}:{index}" (e.g. "fanout@2:0"). Tests for round-trip
  and parse rejections.
- fabro-types/src/lib.rs: re-export ParallelBranchId.
- fabro-types/src/run_event/mod.rs: RunEvent, RunEventRaw, and
  RunEventParts take Option<StageId> / Option<ParallelBranchId>.
  from_ref gains a small generic opt_field<T: Deserialize> helper
  that also replaces the bespoke actor null-handling branch. to_value
  uses serde_json::to_value(value) for the three typed fields.
- fabro-workflow/src/event.rs: Event::Stage{Started,Completed,
  Failed,Retrying} and Event::Agent take Option<StageId> /
  Option<ParallelBranchId>. Event::ParallelBranch{Started,Completed}
  take the required (non-Option) typed forms. StoredEventFields
  and stored_event_fields() plumb the newtypes end-to-end.
- fabro-workflow/src/context.rs: WorkflowContext::parallel_group_id()
  returns Option<StageId>, parallel_branch_id() returns
  Option<ParallelBranchId>. Read via serde_json::from_value which
  validates the shape on the way out.
- fabro-workflow/src/handler/parallel.rs: builds typed values
  directly, stores in context via serde_json::to_value (still
  produces a JSON string through the custom Serialize). BranchSetup
  holds a ParallelBranchId.
- fabro-workflow/src/handler/llm/api.rs: StageEventScope holds
  typed ids.
- fabro-workflow/src/lifecycle/event.rs: stage_parallel_ids returns
  typed tuple.

Wire JSON is byte-identical before and after (StageId serializes as
"{node_id}@{visit}", ParallelBranchId as "{node_id}@{visit}:{index}",
matching the existing spec). Progenitor-generated types and OpenAPI
schema untouched. Existing None-only fixtures in runtime_store,
git, pipeline, error, run_state, rewind, pr_view, and store/dump
didn't need any edit because None fits any Option<T>.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:39:40 -04:00
Bryan Helmkamp
ea84d9bc72
refactor(store): flatten EventEnvelope wire shape via serde
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>
2026-04-09 12:09:00 -04:00
Bryan Helmkamp
747ccc0383
refactor(events): tidy schema v2 plumbing
Quality cleanup on top of the v2 envelope commits:

- fabro-workflow/src/event.rs: add ActorKind/ActorRef/RunProvenance
  to the existing ::fabro_types import block so call sites can use
  unqualified names (restores CLAUDE.md import style). Extract a
  node_stored_fields helper to collapse 4 near-identical match arms
  in stored_event_fields. Drop the no-op ..default() from the Agent
  arm where all 9 fields are set explicitly.
- fabro-types/src/run_event/mod.rs: collapse 9 copies of the
  obj.get/as_str/to_string chain in from_ref behind an opt_str
  closure.
- fabro-server/src/server.rs: dedupe the two identical error
  closures in api_event_envelope_from_store. Skip the typed
  ApiEventEnvelope roundtrip in sse_event_from_store so streamed
  events go straight from the wire Value to a JSON string.
- fabro-workflow/src/handler/llm/api.rs: inline current_visit into
  its sole caller current_stage_event_scope.

Also fixes pre-existing test compile breakage carried in by the
v2 commits: restore the fabro_types::RunId import in support.rs
(removed by b51403ae but still referenced by find_run_dir), and
thread parallel_group_id/parallel_branch_id: None through 9
Event::Stage*/Event::Agent constructors in run_progress and
store/dump tests that 91d61016 missed.

No behavior change aside from the SSE hot path avoiding one full
strong-type deserialize + reserialize per event.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:42:02 -04:00
Bryan Helmkamp
b51403ae64
refactor(events): simplify envelope metadata plumbing
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.
2026-04-09 11:08:38 -04:00
Bryan Helmkamp
51dc4350a5
feat(api): flatten EventEnvelope wire JSON (schema v2)
Wire EventEnvelope now inlines the RunEvent payload fields alongside
seq at the top level of the JSON object. The internal Rust
EventEnvelope { seq, payload } stays structurally unchanged; only the
API/SSE serialization layer flattens for clients.

- OpenAPI spec: add stage_id, parallel_group_id, parallel_branch_id,
  tool_call_id, actor to RunEvent; model EventEnvelope as allOf(seq,
  RunEvent); introduce ActorRef/ActorKind schemas.
- fabro-server: rewrite api_event_envelope_from_store to merge seq
  into the payload JSON value before returning the generated flat
  type; remove the now-unused nested ApiRunEvent conversion helper.
- fabro-cli server_client: add wire_event_envelope_into_store helper
  that turns flat wire JSON back into fabro_store::EventEnvelope
  { seq, payload } for internal consumers.
- Regenerate progenitor Rust types and typescript-axios client.
- Update demo stubs, SSE tests, CLI test helpers, and insta
  snapshots to expect the flattened shape and the new stage_id field.

Incidental: the typescript regeneration also picked up prior-merged
spec fields (ApiQuestion stage/timeout/context, upload manifest
batches, web-settings) that were stale in the TS client.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:40:21 -04:00
Bryan Helmkamp
b6eb462a05
feat(workflow): populate new envelope fields in stored_event_fields
Populates stage_id, parallel_group_id, parallel_branch_id,
tool_call_id, and actor on RunEvent from the internal Event
variants:

- stage_id on stage.* events ("{node_id}@{visit}")
- parallel_group_id on parallel.* events ("{node_id}@{visit}")
- parallel_group_id + parallel_branch_id on parallel.branch.*
- tool_call_id + stage_id on agent.tool.* events
- actor=User from run.created provenance.subject.login
- actor=Agent{session_id, model} on agent.message events

Adds unit tests covering each extraction path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:21:12 -04:00
Bryan Helmkamp
91d610162b
feat(workflow): carry visit + parallel group/branch ids on stage events
Adds visit: u32 to Event::StageStarted/Completed/Failed/Retrying so
stored_event_fields() can derive stage_id = "{node_id}@{visit}".
Adds parallel_group_id/parallel_branch_id to ParallelBranchStarted/
Completed Events, computed once in handler/parallel.rs from the
parent parallel node id + visit_from_context + branch index.
Emission sites in lifecycle/event.rs populate visit from
state.node_visits via a new stage_visit helper.

Stored_event_fields() still leaves stage_id and parallel ids None
pending the extraction pass in the next commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:14:45 -04:00
Bryan Helmkamp
07f28dd9d5
feat(types): add RunEvent envelope fields + ActorRef (schema v2)
Adds stage_id, parallel_group_id, parallel_branch_id, tool_call_id,
and actor to RunEvent per the v2 concrete-shape proposal. Introduces
ActorRef/ActorKind types. Serialization omits absent fields rather
than writing null. Stubs StoredEventFields with matching defaults;
population in stored_event_fields() follows in a later commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:03:01 -04:00
Bryan Helmkamp
bc7a926e26
feat(artifacts): remove scratch artifact cache staging
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.
2026-04-08 17:42:55 -04:00
Bryan Helmkamp
8710643fc9
fix(test): remove dist/ dependency from source_maps_are_not_served test
Use a synthetic .map path instead of scanning apps/fabro-web/dist at
runtime, which requires a prior bun build and breaks on fresh checkouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:51:23 -04:00
Bryan Helmkamp
b3226c84b5
test(cli): add integration tests for fabro uninstall
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>
2026-04-08 16:47:48 -04:00
Bryan Helmkamp
7dd93b7a28
fix(test): remove workspace test build warnings
Drop the unused test-only import in fabro-server and the unused test
binding in fabro-workflow so workspace test builds stay warning-free.
2026-04-08 16:47:23 -04:00
Bryan Helmkamp
b6dddfb6a3
fix(cli): restore fabro-cli verification after merge
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.
2026-04-08 16:41:13 -04:00
Bryan Helmkamp
9305b57bd2
Merge remote-tracking branch 'origin/main' 2026-04-08 16:32:44 -04:00
Bryan Helmkamp
0344cffce2
feat(cli): add fabro uninstall command
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>
2026-04-08 16:31:32 -04:00
Bryan Helmkamp
622760952d
Merge remote-tracking branch 'origin/main' 2026-04-08 16:30:26 -04:00
Bryan Helmkamp
8c6729c186
refactor(cli): centralize command settings and server access
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.
2026-04-08 16:27:25 -04:00
Bryan Helmkamp
13bcac88dc
refactor: remove negative scratch assertions and rename child dir to stages/
Remove test assertions that verified legacy files (final.patch,
workflow_bundle.json, manifest.json, cache/artifacts/values/) do not
exist in scratch directories — these are a test smell since the code
that wrote them is long gone.

Also rename child workflow scratch path from nodes/{id}_{visit}/child
to stages/{id}@{visit}/child to align with stage_id convention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:23:02 -04:00
Bryan Helmkamp
b101bfd5d5
feat(server): make web ui optional
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.
2026-04-08 16:22:41 -04:00
Bryan Helmkamp
552d46398e
Merge remote-tracking branch 'origin/main' into production-web-ui 2026-04-08 16:07:38 -04:00
Bryan Helmkamp
a54e27f42e
fix: resolve clippy warnings across workspace
- 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>
2026-04-08 15:56:51 -04:00
Bryan Helmkamp
cbcc0dad62
fix(test): eliminate session lock race and openssl /dev/stdin flake
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>
2026-04-08 15:48:51 -04:00
Bryan Helmkamp
fdb1297442
feat(server): support host-only tcp binds
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.
2026-04-08 15:43:09 -04:00
Bryan Helmkamp
5d5a84d229
fix(workflow): cancel devcontainer lifecycle commands on shutdown
Pass the run-scoped cancellation flag into devcontainer lifecycle
commands so startup shutdown interrupts those commands promptly and
preserves the cancelled workflow result. Add workflow regression tests
for cancelled setup and devcontainer startup paths.
2026-04-08 15:06:11 -04:00
Bryan Helmkamp
1879d698f2
fix(workflow): cancel setup commands during startup shutdown
Reuse the existing sandbox cancellation bridge for workflow setup
commands so server-side startup cancellation interrupts setup work
promptly and preserves the cancelled terminal state under nextest.
2026-04-08 14:58:32 -04:00
Bryan Helmkamp
8bab1335c9
Merge remote-tracking branch 'origin/main' into production-web-ui 2026-04-08 14:44:35 -04:00
Bryan Helmkamp
c58b731171
feat: embed fabro spa and align interruption semantics
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.
2026-04-08 14:43:44 -04:00
Bryan Helmkamp
35fac654c3
fix(test): reduce test server stop timeout from 8s to 500ms
The test harness waited 8s for the server to shut down gracefully,
accommodating the server's 5s WORKER_CANCEL_GRACE. But in tests,
the CLI returns before workers exit (terminal SSE event → CLI exits →
TestContext drops → SIGTERM while workers still cleaning up), so the
last test in every session paid a ~5s penalty. No real work needs
preserving in tests, so SIGKILL after 500ms instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:29:00 -04:00
Bryan Helmkamp
9ae606cfc1
fix(test): use no_proxy client in detach signal test to prevent macOS timeout flakes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:51:34 -04:00
Bryan Helmkamp
c4b51e44ea
fix: session cookie decryption and add HTTP endpoint logging
Cookie auth was broken because parse_cookie_header used Cookie::parse
which does not percent-decode values. The cookie crate's private jar
percent-encodes on Set-Cookie but Cookie::parse leaves %2F/%3D intact,
making base64 decryption fail silently. Switch to Cookie::parse_encoded.

Also:
- Add tower-http TraceLayer for request/response logging (DEBUG for
  requests, INFO for responses with status and latency)
- Add structured tracing to all web_auth handlers per logging strategy
- Replace eprintln debug calls with tracing::warn
- Update GitHub App manifest homepage URL to https://fabro.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:46:30 -04:00
Bryan Helmkamp
90b373c366
fix(test): eliminate recovery, cancel, and label flakes
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.
2026-04-08 10:13:17 -04:00
Bryan Helmkamp
c663beb18a
fix: GitHub App setup flow — nullable webhook_secret, double POST, stale restart copy, wrong port
- Change webhook_secret to Option<String> in GitHubManifestConversion since
  GitHub's API returns null when no webhook URL is configured
- Use useRef guard to prevent React StrictMode from firing the one-time
  manifest conversion POST twice
- Remove fake "restart required" flow — server reads auth config lazily so
  no restart is needed after setup
- Derive web.url and api.base_url from the request Origin header instead of
  hardcoding port 3000
- Add error logging for manifest conversion parse failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:34:39 -04:00
Bryan Helmkamp
e37d20bd6f
test fix 2026-04-08 07:55:32 -04:00
Bryan Helmkamp
007128ceb9
feat: wire web UI to real server with demo mode toggle
Server changes:
- Add /boards/runs to demo routes (delegates to list_runs)
- Fix demo get_run_status to return StoreRunSummary shape matching OpenAPI spec
- Enrich real /boards/runs to return RunListItem shape with board column mapping
  (Running->working, Paused->pending, Completed->merge; others excluded)
- Update existing tests that asserted old RunStatusResponse fields from /boards/runs

Web UI changes:
- Add DemoModeProvider context and useDemoMode hook
- Hide Workflows/Insights nav items in production mode via getVisibleNavigation
- Change run-detail loader to use /runs/{id} directly instead of searching /boards/runs
- Add mapRunSummaryToRunItem for mapping server response to UI shape
- Add Graph tab, hide Stages tab in production mode, always hide Files tab
- Make run-overview and run-graph loaders resilient to 501 via apiJsonOrNull
- Add isNotImplemented and apiJsonOrNull helpers to api.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 04:46:08 -04:00
Bryan Helmkamp
6a5f6024a2
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-cli/src/server_client.rs
2026-04-08 03:53:54 -04:00
Bryan Helmkamp
57ec693a90
test(cli): replace slow active-run coverage
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.
2026-04-08 03:47:35 -04:00
Bryan Helmkamp
057575baa2
test(cli): move timeout-prone run coverage into ITs
Replace bin-scoped localhost HTTP tests with command-facing integration
coverage so they run under the intended IT timeout budget without
changing nextest overrides.
2026-04-08 03:40:48 -04:00
Bryan Helmkamp
c2e5d465e1
Merge remote-tracking branch 'origin/main' 2026-04-07 23:44:15 -04:00
Bryan Helmkamp
5cc4c92c63
fix(cli): follow paginated run events 2026-04-07 23:43:25 -04:00
Bryan Helmkamp
57cc38894b
fix(server): skip delete grace for terminal runs
Completed runs can briefly retain a stale worker PID after their terminal
state is visible. Using the full 5s worker cancellation grace in that window
made rm and prune pay an avoidable delay.

Keep the existing grace for active runs, but use a short delete grace for
already-terminal runs so completed-run cleanup stays fast.
2026-04-07 23:43:06 -04:00
Bryan Helmkamp
1b9de04b33
test(cli): narrow system prune coverage for speed 2026-04-07 23:42:15 -04:00
Bryan Helmkamp
a308ada2db
test(checkpoint): restore RunRecord helper fields 2026-04-07 23:24:36 -04:00
Bryan Helmkamp
3c801f1665
Merge remote-tracking branch 'origin/main' 2026-04-07 23:16:15 -04:00
Bryan Helmkamp
d634f77951
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-types/src/run_event/run.rs
2026-04-07 23:15:06 -04:00
Bryan Helmkamp
8bf619a095
Merge remote-tracking branch 'origin/main' 2026-04-07 23:13:56 -04:00
Bryan Helmkamp
3c9c8cabf0
refactor(artifacts): clean up stale artifact wording
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.
2026-04-07 23:13:22 -04:00
Bryan Helmkamp
cde5aaed34
test(cli): make server_start cleanup non-graceful 2026-04-07 23:13:11 -04:00
Bryan Helmkamp
4a417b6013
refactor(run): simplify CAS-backed run definitions
Drop compatibility versioning from run-definition blobs, remove the
read-after-write polling added around CAS access, and tighten tests to
assert workflow_bundle.json is never written.
2026-04-07 23:10:00 -04:00
Bryan Helmkamp
46232503dc
fix(api): resolve build spec path at runtime 2026-04-07 23:05:07 -04:00