Commit graph

290 commits

Author SHA1 Message Date
Scott Werner
88dae80c6d Consolidate automation environment helpers and trim redundant work
- Share the clone-based provider predicate and provider label between the
  automation form and environment settings instead of duplicating them
- Hoist repeated environments query state in the new-automation route
- Normalize empty environment ids to None so validation needs one check
- Merge the scheduler's record/clear error helpers and skip the clearing
  write when no error is stored
- Guard the environment backfill with a cheap existence query
- Drop an unneeded id clone and a no-op migrator comment

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 12:25:16 -04:00
Scott Werner
e87130ae23 Require a server-managed environment for automations
Automations now store an environment_id that must reference an enabled
Docker or Daytona environment. Each trigger fire resolves the current
environment definition and snapshots its settings into the run, and
deleting an environment still referenced by an automation is rejected
with a conflict.

Existing automations are backfilled conservatively: a compatible
environment named default is selected when present, otherwise the sole
compatible environment. Anything ambiguous is left incomplete and cannot
run until an operator selects an environment in the web UI.

Scheduler failures are recorded on the automation as last_error and
cleared after the next successful scheduled run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 09:58:13 -04:00
Bryan Helmkamp
9dd47a8cd3
Merge pull request #819 from fabro-sh/codex/daytona-image-snapshots
Allow Daytona snapshots from Docker images
2026-08-28 16:24:55 -04:00
Scott Werner
5cd69e9c66
Merge pull request #820 from fabro-sh/codex/automation-run-intent
Move automation runs to workflow versions and RunIntent
2026-08-28 15:43:31 -04:00
Scott Werner
9bd499cdbe
Merge pull request #817 from fabro-sh/codex/run-record-sql-foundation
Some checks failed
Rust / Test (Linux) (push) Has been cancelled
Rust / Test (macOS) (push) Has been cancelled
Rust / Format (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Generated Docs (push) Has been cancelled
TypeScript / Typecheck (push) Has been cancelled
TypeScript / Test (push) Has been cancelled
TypeScript / Build (push) Has been cancelled
Add inactive SQL run event storage foundation
2026-08-27 16:30:05 -04:00
Scott Werner
077d020223 Simplify automation workflow-version packaging
Share the RunIntent shape between the scheduler and the API trigger via
AutomationRunMaterialized::into_run_intent, drop the pass-through
packaging wrappers and the unreachable VersionIdMismatch error, and move
the config-path and version-ID derivations onto WorkflowVersion so the
server, validator, and collector stop re-deriving them.

The collector now owns the collected sources (moving file contents
instead of cloning them), resolves the workflow location once, and shares
the not-found probe with build_run_manifest. The bundler reads a goal
file once instead of twice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:29:45 -04:00
Scott Werner
57bbb923c2 Keep the RunSummaryStore name until the SQL cutover
Revert the run summary -> run record rename. The SQLite store is still
the summary read model today; it only grows an inactive events table
here. Renaming it now made the store file show as a delete plus add and
touched nine unrelated files. The final rename happens once, when the
SQL store becomes the run authority.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 15:43:56 -04:00
Scott Werner
e86dd3bea4 Move automation runs to workflow versions 2026-08-27 15:24:07 -04:00
Bryan Helmkamp
b34f2b8207
Allow Daytona snapshots from Docker images 2026-08-27 15:02:50 -04:00
Scott Werner
ff2aef4564 Simplify SQL run record store write path and test fixtures
Share one bind helper across the runs insert/upsert/update statements,
compute the next event sequence once per append, and decode stored
sequence columns through a single helper. Check the run head before
decoding events, rewrite the first-visit stage listing as a UNION ALL so
each arm uses its partial index, and share the run_events insert SQL
with the test seeder.

Collapse the duplicated in-memory pool fixture, remove two tests that
only asserted Arc sharing, and fold the fabro-db test row helpers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 14:59:34 -04:00
Scott Werner
b62e458289 Add inactive SQL run storage foundation 2026-08-27 13:32:01 -04:00
Scott Werner
50fed849f3 Simplify automation run-target plumbing
- Materializer derives the manifest GitContext from RunTarget::validate()
  instead of hand-building it and re-parsing the repository slug
- Drop parse_github_repository_slug and InvalidRepositorySlug, now unused
- Store reuses Automation::git_target() instead of a private duplicate
- Legacy TOML import returns the target directly rather than a tuple
- Automation target migration updates columns with a single UPDATE ... FROM
- Web: share gitTarget(), targetFromFormValues(), and one SHA validator
  across the automation form, list, detail, new, and edit views

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 18:17:22 -04:00
Scott Werner
a65c4ff779 Migrate automations to canonical run targets 2026-08-26 17:35:36 -04:00
Scott Werner
56e759d470
Merge pull request #812 from fabro-sh/codex/git-run-target-tags
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
Add tag support to Git run targets
2026-08-26 15:20:23 -04:00
Bryan Helmkamp
eaba019acb
Warn when run title generation fails 2026-08-26 13:18:39 -04:00
Scott Werner
fbba98defd Add tag support to Git run targets 2026-08-26 11:16:37 -04:00
Scott Werner
34014e6dce
Merge pull request #790 from fabro-sh/codex/run-intent-folder-target
Add local folder run target
2026-08-26 10:34:59 -04:00
Bryan Helmkamp
6ac769c4cd
Merge pull request #810 from fabro-sh/mcp-config-name
Add --name to fabro mcp config and fabro mcp init
2026-08-26 09:22:49 -04:00
Bryan Helmkamp
e5046d8b1b
Refactor MCP config argument handling 2026-08-26 09:01:37 -04:00
Bryan Helmkamp
a049f94042
Add on_failure="succeed" as an explicit failure policy
A failed node with an effective `succeed` policy and no explicit recovery
route now finishes as `succeeded` and follows normal success routing. The
original failure stays on the outcome so the stage.completed event and the
checkpoint keep the diagnostic, and the outcome notes record which scope
promoted it.

- OnFailure gains a Succeed variant; Node::on_failure resolves the
  deprecated auto_status=true attribute as an alias, with an explicit
  on_failure winning
- The core executor applies the policy before the lifecycle observes the
  result, so the recorded outcome, context keys, goal gates, events, and
  routing all see the effective outcome; this replaces AutoStatusLifecycle
- Explicit routes take priority: a matching condition, preferred label,
  suggested next node, or handler jump keeps the outcome failed. A failed
  outcome takes an unconditional edge only under route, so under succeed
  any edge selection is an explicit route
- succeed applies only to failed, matching exit; the auto_status alias no
  longer promotes partially_succeeded
- Parallel branches promote after their retry loop, so a failed succeed
  branch counts as succeeded in the parent aggregate
- Validation accepts succeed and adds an auto_status_deprecated warning
  that suggests on_failure="succeed"
- Document the policy table, semantics, and deprecation; add a changelog
  entry

Closes #807

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 07:34:24 -04:00
Bryan Helmkamp
24165b10f5
Add --name to fabro mcp config and fabro mcp init
Both commands always registered the MCP client entry under the fixed
`mcpServers` key `fabro`, so users could not register separate Fabro
servers (for example production and testing) without editing the client
JSON by hand.

`--name <NAME>` now selects the `mcpServers` key. It defaults to `fabro`
for backward compatibility and rejects empty values. `fabro mcp init`
upserts only the named entry and preserves entries with other names, so
reusing a name updates that entry in place.

Closes #808

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 07:34:06 -04:00
Bryan Helmkamp
c90d195c2f
Merge pull request #806 from fabro-sh/node-on-failure
Add node-level on_failure override
2026-08-26 07:01:49 -04:00
Bryan Helmkamp
491babe5da
Add node-level on_failure override
A node can now set its own on_failure attribute to override the
graph-level failed-node routing policy in either direction: a
best-effort node can keep route inside an exit graph, and a critical
node can exit while the rest of the graph keeps the default. An absent
node attribute inherits the graph policy.

- Node::on_failure returns Option<OnFailure> so absence means inherit
- Graph::resolve_on_failure(node_id) is the single resolution point,
  returning ResolvedOnFailure { policy, scope } so the executor's
  end-of-run message names the scope that stopped routing
- The core Graph trait method becomes resolve_on_failure(node_id); the
  graph-scope failure message is unchanged
- The failed-human-gate fallthrough block stays independent of a
  node-level route override
- Validation now accepts and value-checks node-level on_failure (it
  previously warned that node placement had no effect) and keeps the
  edge-placement warning with updated wording
- Document precedence in transitions, failures, and the DOT reference,
  and extend today's changelog entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Chraa21RK7i2KqHdZSJLb8
2026-08-25 18:55:39 -04:00
Bryan Helmkamp
a522414bdc
Add model stylesheet templates 2026-08-25 18:14:25 -04:00
Scott Werner
ca9fb1d262 Align folder target warn logs with tracing style
Capture the error with Debug so the source chain stays visible, and use
lowercase fixed message strings per the logging guidelines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 15:40:29 -04:00
Bryan Helmkamp
9223349101
Merge pull request #803 from fabro-sh/model-test-tools-reasoning-effort
feat(model): add tool and reasoning test controls
2026-08-25 15:39:18 -04:00
Scott Werner
9dc39ce9fa Simplify folder target admission and startup checks
Collapse the triple Folder dispatch in run-intent admission into a single
prepare_intent_target call that canonicalizes and observes Git under one
provider gate, and stop feeding target/git into the compiler input only to
overwrite them afterwards. In run start, hoist the duplicated Folder
rejection out of the Docker and Daytona arms, restore kind_name() for the
Git/None arm, and drop the unreachable absolute/symlink checks that follow
canonicalize. Dedupe the folder-target test fixtures in both crates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 14:53:31 -04:00
Bryan Helmkamp
32d6be7ea5
refactor(model): simplify model test plumbing
Review cleanups for the tools/reasoning-effort model test change:

- Extract a shared parse_query_enum helper in the models handler in
  place of two copy-pasted parse-or-400 match blocks.
- Collapse the duplicated basic-probe pipeline in fabro-llm behind a
  single basic_probe core; name the shared EXPANDED_MAX_TOKENS budget.
- Pass &ModelTestArgs to test_models_via_server instead of threading
  five of its fields positionally.
- Dedupe the two forwarding CLI integration tests behind a helper.
- Derive clap::ValueEnum for ReasoningEffort behind a feature-gated
  clap dep (same pattern as MergeStrategy in fabro-types) so --help,
  cli.mdx, and error output list effort values from the enum instead
  of a hand-written list that drifts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TDdjG18d2AHh7mFWXFkBLn
2026-08-25 14:49:34 -04:00
Scott Werner
b5d517e4b5 Gate folder path access behind Local admission 2026-08-25 14:10:24 -04:00
Bryan Helmkamp
b4092af89f
Add graph on_failure exit policy 2026-08-25 13:45:51 -04:00
Bryan Helmkamp
5ead0145b9
feat(model): separate tool and reasoning tests 2026-08-25 13:36:51 -04:00
Scott Werner
c396a6cf6f Add local folder run target 2026-08-25 11:59:37 -04:00
Scott Werner
679d20cb52
Merge pull request #792 from fabro-sh/codex/sqlite-authorization-codes
Move pending CLI authorizations to SQLite
2026-08-25 11:46:10 -04:00
Scott Werner
0001cfba02
Merge pull request #789 from fabro-sh/codex/run-intent-none-target
Add empty workspace run target
2026-08-25 11:25:41 -04:00
Scott Werner
dc1f235c48 Keep retired Slate helpers test-only 2026-08-24 17:31:15 -04:00
Scott Werner
f3ff7f27a4 Keep auth code store naming consistent 2026-08-24 17:26:17 -04:00
Scott Werner
68ef8c7e89 Leave old SlateDB authorization-code records in place
Drop the startup retirement of the auth/code keyspace instead of
carrying one-shot cleanup code forever. The records it deleted are
inert: at most a handful exist at cutover, every binary (old or new)
rejects them within 60 seconds of issue via the expiry check, and
nothing reads the keyspace after the move to SQLite. The refresh-token
retirement keeps its original inline shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 17:25:59 -04:00
Scott Werner
a2f0167844 Simplify SQLite authorization-code cutover
Cleanup pass over the pending-CLI-authorization move to SQLite:

- Extract a shared Database::retire_keyspace helper; the refresh-token
  and authorization-code retirements are now one-line wrappers over it.
- Inline the startup retirement call (dropping the single-use wrapper,
  its context-chain test, and the test_close_slate hook it required)
  and run both SlateDB retirement scans concurrently. Error policies
  are unchanged: authorization codes fatal, refresh tokens best-effort.
- Add a shared sqlite_row module with typed identity/timestamp row
  decoding, used by both AuthorizationCodeStore and AuthSessionStore;
  the session store's stringly Error::Other corruption errors become
  the typed InvalidStoredIdentity/InvalidStoredTimestamp variants.
- Delete Repository::gc, which had no production callers left and was
  kept alive by its own test; update the record-layer docs to match.
- Deduplicate the SQLite test-support bootstrap into sqlite_test_pool,
  reuse issue() in the invalid-timestamp test instead of a copied
  INSERT, and fold the new table into the existing existence-check loop
  in the fabro-db schema test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 17:25:38 -04:00
Scott Werner
05999036aa Move pending CLI authorizations to SQLite 2026-08-24 17:25:19 -04:00
Scott Werner
3e0adde73d Simplify the empty workspace run target plumbing
- Use a derived deserializer for RunTarget by making `None` an empty struct
  variant, which keeps `deny_unknown_fields` strict without a hand-rolled impl
- Make clone_source_for_run the single owner of the empty-workspace decision
  and drop the duplicated target checks in RunSession::new
- Collapse duplicated target/provider compatibility matches in admission and
  start into single matches, using a strum-derived kind name for messages
- Drop the redundant git override in persist_create_run
- Extract a shared helper for the duplicated unavailable-integration test loop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 17:21:54 -04:00
Scott Werner
2d292c28f8
Merge pull request #783 from fabro-sh/codex/sqlite-blob-startup-activation
Activate verified SQLite blob storage at server startup
2026-08-24 16:32:59 -04:00
Scott Werner
2f3b6477f2 Add empty workspace run target 2026-08-24 14:49:03 -04:00
Scott Werner
fb833294bf Remove stale auth-session test import 2026-08-24 14:40:38 -04:00
Bryan Helmkamp
c042b9abdc
Merge remote-tracking branch 'origin/main' into feature/bounded-agent-tool-output
# Conflicts:
#	lib/components/fabro-sandbox/src/clone_source.rs
2026-08-24 14:08:04 -04:00
Scott Werner
0e580f0a43 Adapt run intents to activated blob storage
Use the synchronous blob authority established at server startup and remove the obsolete per-request store-open error path.
2026-08-24 14:07:51 -04:00
Scott Werner
776e719383 Harden SQLite blob activation safety
Keep VACUUM snapshots private until permissions and durability are established. Refuse to recreate a missing rollback backup after import has begun, and preserve secondary cleanup failures in startup logs.
2026-08-24 14:02:35 -04:00
Scott Werner
f71d077221 Register the SQLite blob activation bridge as a server migration
The activation module described itself as a temporary compatibility
bridge but bypassed the structure the migrations strategy prescribes: no
dated migrations/ file, no src/migrations.rs registry entry, no
REMOVAL_DEADLINE, and no removal_deadline log field. The strategy doc's
removal checklist (grep REMOVAL_DEADLINE, explicit registry ordering)
would never have surfaced it, letting the bridge silently outlive its
window as a second, parallel migration mechanism in serve.rs.

The module now lives at migrations/2026082301_sqlite_blob_activation.rs,
is registered and re-exported through src/migrations.rs like the two
existing server migrations, carries a REMOVAL_DEADLINE eligibility floor
(removal still requires the evidence and explicit approval in the module
docs), and logs removal_deadline on every activation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 14:02:35 -04:00
Scott Werner
5629dcd7d0 Make snapshot and backup publication durable across power loss
Neither the pre-activation backup nor the pre-migration snapshot fsynced
the staged file contents or the parent directory around the publishing
rename. A crash after the import committed could lose the retained
'.pre-blob-activation.bak' (whose directory entry was never made
durable), and the next activation would then write a new backup that
already contains the imported blobs, silently breaking the documented
pre-activation rollback boundary; a torn staging file could likewise
wedge later boots in backup validation.

write_snapshot_to_staging now syncs the staged file before handing it to
the caller, and both publishers sync the destination's parent directory
after their rename (fabro-db on a blocking task, activation inside its
existing blocking publication task).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 14:02:35 -04:00
Scott Werner
e067de9382 Share one SQLite snapshot-staging helper between fabro-db and activation
create_backup re-implemented the staging half of fabro-db's
pre-migration snapshot (remove stale staging file, UTF-8 check,
VACUUM INTO, private permissions), and remove_file_if_exists and
set_private_permissions had been made pub precisely to hand-copy that
sequence. Any future hardening of snapshot staging would have had to
land in two crates and could drift.

fabro-db now exposes write_snapshot_to_staging with a typed
SnapshotStagingError; both the pre-migration snapshot and the
pre-activation backup stage through it, and the hand-copied helpers are
private again. The publish halves stay separate on purpose: migrations
overwrite their snapshot, activation publishes with persist_noclobber
plus integrity validation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 14:02:35 -04:00
Scott Werner
31c7a670d5 Continue startup when the final WAL truncate checkpoint reports busy
PRAGMA wal_checkpoint(TRUNCATE) returning busy=1 aborted server startup.
Any external reader that outlives the pool's five-second busy timeout (a
replication agent, a backup tool, an operator sqlite3 shell) would crash
the boot, and a supervisor restart would loop into the same abort while
the reader persisted, over a condition that threatens no data integrity.

A busy truncate now logs a warning and startup continues; a later
checkpoint truncates the WAL once the reader is gone. Adds the
failure-path coverage the relocated checkpoint lost: a held read
snapshot blocks the truncate and activation still succeeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 14:02:35 -04:00