Retry forked the source run at its last checkpoint and reran the failed
stage. It now creates a new run from the source's saved spec and starts
the workflow from the beginning in a fresh workspace, as retry did
before the Petri cutover. The new run records `retried_from` and no
`fork_source_ref`.
Retry no longer needs a checkpoint, a retained workspace, or a published
run branch, so it works for any terminal run that is not archived. To
continue from where a run stopped, fork it at a checkpoint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docs deployment has failed on every main push since the checkpoint
endpoint was removed: the API navigation still listed
`GET /api/v1/runs/{id}/checkpoint`, and Mintlify refuses to build a
navigation entry the OpenAPI spec no longer has. Drop the entry.
`mintlify validate` also rejected the settings reference, where MDX read
the value type `table<string, array<string>>` as a JSX tag. The options
reference generator now writes angle-bracket types as code, and the
reference is regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The store checks that captured bytes match their digest in every build,
hashing on a blocking thread, and the upload handler relies on that
check instead of hashing a second time.
- Capture bytes travel as `Bytes` from the hooks through the client and
the store, so uploads and retries share one buffer.
- The artifact writer takes the run ID from the hooks, so objects are
stored under the run their records name.
- Concurrent captures of the same file and content wait on one another,
so the file is uploaded and recorded once.
- When a record append fails, the hooks re-read the run's captures and
treat a record that did land as done, so a lost response does not
record the capture twice.
- An upload that finishes after its run was deleted removes itself,
instead of leaving an object nothing references.
- Listing a run's stage artifacts skips everything under `captures/`, so
an unexpected object there cannot fail the listing or the ZIP.
- The capture record derives its `digest` key from its source instead of
storing it twice, still writing and checking it on the wire.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Servers have always moved a local artifact store under the storage
directory at startup, whatever `local.root` said. Browser-wizard installs
write `local.root = "<storage>/objects"`, so honoring that root would move
their store and hide every artifact already written, with nothing to
migrate it. Restore the storage-directory override for local roots and
leave honoring custom roots to a change that migrates existing objects.
Installer metadata still goes through the override, so it lands where the
server reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The artifact writer takes the digest the hooks already computed, so
captured bytes are hashed once on each side, and the dead integrity
error goes away.
- The writer is a required part of HooksSpec, not an optional field on
RunRequest, so a run with capture globs always has a writer and the
no-writer error goes away.
- ArtifactStore routes put/get and the capture methods through shared
put_at/get_at helpers.
- The upload handler parses the digest with parse_blob_hash_path before
any store reads, and builds its size-limit message from the constant.
- The default local artifact root comes from one helper used by both
config resolution and the storage-dir override.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release workflow already runs the whole suite in a release build,
which includes the built-in Host run and prune scenario.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Keep plugin-era Daytona lease fingerprints: read only DAYTONA_API_URL and
DAYTONA_ORGANIZATION_ID (no URL alias, no placement target), and stop
forwarding DAYTONA_SERVER_URL and DAYTONA_TARGET to the worker.
- Take the Docker fingerprint and network from this process's DOCKER_HOST,
the endpoint the Docker client actually connects to; make the provider
configuration's fields private.
- Return an error instead of panicking when Petri supplies no Host registry.
- Run deletion reads the Daytona key only for a Daytona run, and a forced
or restarted delete goes on when the secret store fails, as it does for
every other prune failure.
- Stop putting DAYTONA_API_KEY in the worker's environment; the worker reads
it from the vault. Give the worker's Daytona client the shared HTTP client.
- Fork, rewind and retry no longer read the vault: a fork acquires no sandbox.
- Remove the dead worker plugin forwarding and document that runs execute
only on the built-in providers.
- Build every Petri runtime through providers::standard_runtime or
bare_runtime, with a Clippy lint against Runtime::standard/bare.
- Share the Docker require-or-skip policy in fabro-test, tighten the Host
scope assertion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Load the Daytona key for fork and prune through one AppState method
instead of two copied vault reads, and pass the sandbox configuration
into runtime_spec rather than building it and overwriting it. The
worker reuses the CLI's process_env_var lookup.
Share one Docker availability check and the backend-requirement
variable through fabro-test, drop the built-in plugin path and pin
constants nothing reads any more, and let enabled_plugins() exclude the
bundled kinds itself. Refresh the comments and the spawn_env test that
still described built-in plugins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Register lazy Host, Docker, and Daytona factories for Petri execution,
fork, and prune. Share server provider configuration, preserve lease
fingerprints, and source Daytona credentials from the vault.
Remove built-in plugin setup and skip gates; add a release-mode worker
and prune regression to catch the failure that blocked nightly builds.
Co-Authored-By: Codex <noreply@openai.com>
Pebble, Petri, and sandbox-driver now name their internal dependencies by
branch = "main", so move the lock to their mains: pebble 72a51ea, Petri
cbab2c5, sandbox-driver 236196e (the Daytona cursor-listing fix plus a
test-only MSRV fix and a dependency-spelling change), twins 19bf6ae.
lithos-llm stays at 43a42ac: its main has changed Observer::on_retry to
take a RetryEvent, and pebble doesn't build against that yet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reduce the workspace Cargo.toml convention block to three lines: Lithos
libraries track `main`, Cargo.lock picks the commits (move one with
`cargo update -p <crate>`), and unmerged library work is tried with an
uncommitted `[patch]`. Drop the instruction to hand-review lockfile diffs
and trim the restatements in the pebble and petri comments, the
fabro-petri README and module doc, AGENTS.md, and the docker test doc.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every lithoscomputer git dependency (sandbox-driver, pebble, petri,
lithos-llm, twins) now uses `branch = "main"` instead of an exact rev,
matching the libraries, so the workspace resolves one Cargo source per
repository. Cargo.lock is the single place the commits are chosen; move
one with `cargo update -p <crate>`.
The lockfile keeps every commit except sandbox-driver, which moves from
583a164 to b30203c: Daytona removed its paginated sandbox listing, and
b30203c lists through cursors instead (it also moves the driver's
daytona-sdk-rust dependency to 0e69058). The Daytona auth-probe test
mocks now serve the cursor endpoint the driver calls.
CI reads the sandbox-driver commit for the plugin install from the
lockfile through cargo metadata instead of from Cargo.toml.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fabro #893 merged before petri#36, so main pinned both at their PR
heads. Same trees; only the pinned revisions move to the merge commits.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
main (#891) upstreamed the Pebble sandbox adapter and deleted
fabro-pebble-sandbox, so Fabro now builds Pebble's sandbox-driver feature
beside Petri's crates and must hold one sandbox-driver copy. The three
pins move together: sandbox-driver to its main after #61 (host
attach-by-path, merged), Pebble to pebble#27's head after it moved its
own sandbox-driver pin to the same revision, and Petri to petri#36's
head, which carries both bumps.
Resolution: Cargo.toml keeps main's shape with the three revisions
rewritten; Cargo.lock regenerated from main's copy and holds one copy of
each library.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A run's host sandbox is a managed directory the worker recorded, with
Petri's labels, in the host registry inside the run's Petri directory.
The server reached it only by designating the directory again: a handle
with no record, so no labels and no ownership check, unlike Docker and
Daytona where `petri.run` is checked on every attach.
The server now observes the run's registry (`HostProvider::
observe_registry`, read and never written, so the worker stays its only
writer), resolves the directory to its record by path
(`attach_directory`), and runs the same `petri.run` ownership check as
on Docker (`OwnedProvider::check`). The handle refuses every lifecycle
change, so starting, stopping, and deleting stay the worker's, and a
stopped sandbox's retained workspace is usable through it; the access
paths therefore return a host handle as attached instead of activating
it. `ProviderAccess` carries the storage root the registry is found
under; a directory no registry records (a run older than this driver, a
caller without a storage root, a pruned Petri directory) is designated
again as before, without labels, for the read paths only.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`lib/components/fabro-pebble-sandbox` moved into pebble as
`pebble_coding_agent::sandbox_driver` (lithoscomputer/pebble#27): the
`Environment` over a driver handle (`SandboxEnvironment`, was
`PebbleSandbox`), the `SandboxExec` policy, the port routes, and
`display_for_log`. The pebble pin moves to that branch head, 6d03b3b,
with the `sandbox-driver` feature on (`sandbox-driver-test-util` for the
server's tests, which take `MockSandbox` from pebble now). Nothing in the
crate was Fabro's by design; what was Fabro's stays: `SecretRedactor`
moves to `fabro-redact` as pebble's `Redactor` over `redact_string`, and
the log renderer takes it where a driver failure is rendered.
`fabro-petri` hands pebble types to Petri's crates, so Petri must pin the
same pebble revision: the petri pins move to lithoscomputer/petri#36
(9ee3f85), which pins pebble at the same head. Both re-pin to the pebble
merge commit together once #27 merges.
The 14 pebble commits between the pins fold the session projection's
lifetime tallies into `SessionProjection::totals` (and `PromptDelta`'s
into a flattened `totals`, which renames the prompt's `subagents` key to
`subagent_counts`, as the projection's already was). The stage progress
fold, the runs handler, the OpenAPI schema, the generated client model,
and the round-trip test follow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sandbox-driver 7cc5d5ba (lithoscomputer/sandbox-driver#61) adds the
read-only attach by path to a managed host workspace; Petri 46dffa4e
(lithoscomputer/petri#37) pins the same driver revision. Both pins are
pull-request heads and move to the merge commits once those land.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>