18 KiB
Executable File
Архив исходника Broker
Историческая копия из ae92c15 до очистки репозитория 2026-10-16; текущая установка — personal/projects/balda/execution-broker.
Operations and recovery
Use executionctl --token-file … --operation tools for authenticated MCP reachability and execution_status for a specific run. /healthz is liveness only. JSON logs include run IDs and retry causes, without configured tokens. Virfield maintenance/capacity may keep a run provisioning until its deadline; inspect both services rather than creating another idempotency key.
For the local Compose stack, deploy/compose.dynamic-images.yaml pins selected
image tags. Compare those tags with the running containers before claiming a
source change is deployed. A Git push does not rebuild or restart the stack.
For Broker-managed local Git projects, macOS bootstrap automatically scopes the
guest configuration to the run's exact registered repository and enables its
private SSH Git tunnel. It retains the reviewed package digest and workflow pin;
new project names do not require a worker archive rebuild or manual allowlist.
The installed marker covers both the immutable archive and the effective guest
configuration, so a retry repairs an old guest configuration before starting.
Broker checks package workflow/profile compatibility before VM acquisition.
Permanent worker start refusals, including forbidden, workflow_integrity and
bootstrap_incompatible, end the run as failed and notify the original Balda
conversation with diagnostics. Temporary infrastructure failures retain retry
backoff; Telegram approval routing does not suppress terminal errors.
Project tools use operator-owned tooling profiles. An operator
default_tooling entry can preserve a worker's existing standard Go environment;
an explicit task selection pins another approved recipe. SDKs are size/SHA-checked
and installed only in the disposable run directory, with their observed versions
retained in tooling.json. Golden images and the operator host are not modified.
Make/C/C++, Python, Node and JVM use the same package/archive/system backends.
Enable the independent repository cache only for approved sources. Environment changes require an operator-authored safe checkpoint and continuation in the pinned workflow pack; arbitrary graphs are not resumed by replay. Deploy the matching execution/v3 gateway, Runner and bootstrap before enabling either policy. An environment-change terminal outcome must complete old-worker/credential cleanup before its separately approved replacement can allocate anything. Preserve its original deadline and artifacts.
New workers persist whether provider credentials could have been used, and
record a stopped fence only after the Callee process group settles. An unused
preparation lease preserves the issuer cache. Missing admission/process metadata,
a missing used cache or an unknown supervisor outcome leaves the exclusive owner
locked for recovery; an empty phase is not evidence that credentials were unused.
Inspect credential_state_lost, process_group_unsettled and cleanup_error
instead of starting another attempt with the same account.
If a run ends with callee_failed, first read execution_diagnostics using its
bound chat and topic IDs. On updated Broker and worker builds, failure_detail
includes the bounded final adapter failure explanation from a checksum-verified
callee.log, even when no quality.json was produced. Earlier transport failure
classes still distinguish a missing result, process exit or signal, malformed
event, or output read failure. Quality diagnostics retain review and verification
findings separately.
Check the last role event and the callee.log artifact to locate the boundary;
an empty log or generic callee_failed does not prove an agent started or
crashed. Balda must report the cause as unrecorded when diagnostics has neither
a quality finding nor failure_detail; it must not guess from an empty artifact.
For Broker-terminal failures such as lease_failed, the terminal code replaces
any earlier retry code in last_error. Historical runs that ended before this
fix can still show a stale retry code; inspect Virfield lease events and the
prepare job for the actual resource failure. Older deployed builds may not
expose failure_detail; update Broker
and the worker package before relying on it. The Callee adapter accepts an
approval question up to 256 KiB. Runner and the journal retain a complete Human
event up to 512 KiB after JSON serialization; ordinary event summaries retain
their 32 KiB limit. Runner event-processing errors are redacted and saved in
callee.log before artifact collection, so their concrete cause is available to
execution_diagnostics. A
confirmed old 16 KiB
prompt cap requires an updated VM bootstrap package before retrying with a new
Story/run key. A Story created under placeholder 0/0 cannot sync with the real
Balda Session ID; create a new Story and Task bound to the current Session ID.
VM readiness failures
guest_keychain_failed is a Virfield preparation failure before the worker is
ready. Its public error covers native GUI/SSH key storage and unlocked,
unobstructed console checks; it does not identify a password, OAuth or provider
failure. A failed prerequisite can leave no Runner/Callee log or artifacts.
Correlate the Broker run with the exact Virfield lease, prepare job and events.
Preserve bounded private provisioning diagnostics before cleanup when available,
within the existing deadline; never keep a failed lease indefinitely for logs.
After release its private directory may already be gone. Retain the public
failure and confirmed release, and record the unavailable diagnostic scope.
The support matrix records the production macos27 reacceptance passed on 2026-10-16 and preserves the three earlier failed probes. The dated Virfield record defines the tested scope; exact run/job/lease references remain in the private operator incident register. An idle pool, a catalog entry or a different OS/profile's success does not qualify another deployment.
On macOS, kill(-pgid, SIGKILL) can return EPERM for a group containing only
zombies. Runner settles that case only after a separate bounded process
observation finds no executable group members. An observation error or a live
member retains the credential fence; do not clear ownership from the signal
error alone.
Recovery guarantees
Broker stores admission, stable acquisition inputs, every transition and events in SQLite WAL with FULL synchronous writes. It holds a process lock on its data directory. Run one Broker process per database on a local reliable filesystem; SQLite on NFS or multiple independent replicas is unsupported. Newer database schema versions fail closed.
Restart Broker freely: it replays uncertain acquisition/start with the same identity, redelivers unacknowledged Human answers and retrieves missed events. A running Runner stays alive inside its worker when Broker restarts. If the Runner process itself dies, it records a diagnostic failed outcome rather than rerunning an unknown workflow.
For cancellation call execution_cancel, retain the run ID, and wait for terminal and cleanup=done. VM cleanup belongs to Virfield, including Broker downtime. Docker Runner enforces its deadline; gateway independently reaps owned workers one hour after their lease deadline. This grace period permits artifact recovery after an outage. Resource slots remain occupied until release is confirmed. Docker and host-runner retain durable revocation records, so a delayed start cannot recreate a released run. Virfield admission refusals are distinguished from unknown acquisition outcomes; canceling a proven refusal does not create a VM just to delete it. Never manually remove resources belonging to another run to make capacity.
Artifacts resume from a private on-disk prefix after transfer interruption and Broker restart; the complete file is SHA-verified and fsynced before terminal publication. References expire after seven days. An hourly bounded retention pass removes expired payload directories only after confirmed worker cleanup and expiry of every reference. It preserves event, checksum, Story and idempotency history. Historical SQLite/Beads journals remain durable; monitor and size these separately. Partial unpublished transfers are reclaimed seven days after the original execution deadline. Loss of disk space fails transactions and retries without reporting success. Execution stops at its original deadline. Broker and Virfield reserve at most five additional minutes to collect already-written results; provider execution is not extended. Worker-side collection has its own one-minute bound. A lost worker before artifact copy can leave the run awaiting recovery until that bound; missing artifacts are not silently fabricated.
Bootstrap archives in the deployment assets directory and local .build
outputs have no automatic retention pass. After every run pinned to an old
package has reached terminal state and cleanup=done, compare all configured
worker bootstrap_file paths with the archive directory before removing old
packages. Check df after cleanup: APFS clones and local Time Machine snapshots
can retain blocks even when du for the directory shrinks. Virfield reserves
the VM's full virtual disk size plus its host safety reserve at admission; a
run can be rejected while the image's currently allocated size is smaller.
Backup and restore
Use execution-broker --config … --backup /absolute/new-backup.db for a consistent live SQLite snapshot. The destination must not exist. Copy the Broker artifact directory alongside the snapshot; pause new admission for a fully coordinated database/artifact backup. Transport keys are included in the private database: encrypt backups and restrict access to the service administrator.
For Prism/Beads backup, stop the Prism sidecar and take consistent snapshots of both prism-state and beads-state volumes, then start it again. The projection database retains facts/cursors and the Beads directory retains Dolt history; backing up only one is not a complete recovery point.
Restore into an empty data directory with the same original worker profiles, bearer secrets and approval public keys. Stop the existing instance first. Restore the database/artifacts with restricted permissions and restart one service. Verify pending run statuses and resource cleanup before accepting new work. Do not restore an old snapshot while the original instance is still accepting requests; lost post-snapshot idempotency history can duplicate external work.
Balda delivery
Prism projection and chat delivery have independent cursors. An adapter ack means Beads facts were applied, not that a chat message was delivered. Optional Broker notifications routes call Balda's durable inbound webhook with an idempotency header; the destination must deduplicate that header. By default, a 2xx advances the notification cursor. With require_delivery_ack=true, a Balda 202 leaves the event pending and Broker retries the same key; only 200 with a durable provider receipt advances the cursor. The payload contains only run/Story/session/event references; Balda reads authoritative state through MCP before delivering a response. A route may opt into coalesce_cleanup=true: Broker persists whether cleanup was already done before the first terminal webhook attempt. Only that case permits advancing a later cleanup_completed event without another post. Cleanup that finishes during asynchronous delivery is notified separately, including after a Broker restart; a later receipt cannot retroactively change what the terminal message said. The default remains separate notifications.
The packaged Balda skill persists each attempt and arms a native original-session
one-shot wait before execution admission. It uses execution_lookup after a lost
Start/retry response and closes abandoned admissions atomically before declaring
that no execution can arrive later. Each wake arms a fresh generation before
reading Broker state; a separate TTL recovery wait covers a failed intermediate
model turn. Consult Balda integration for the
upgrade contract. A wait acknowledgement is not a chat-provider delivery receipt.
Monitor failed observer turns and retain run IDs for manual recovery.
Current generic Balda webhooks execute in isolated private jobs. Retired session envelope fields are invalid; a wildcard route cannot continue arbitrary sessions. Disable obsolete notification routes while preserving their cursors and audit history. Do not mark retained notifications delivered as part of a migration. Existing Balda sessions pin capability snapshots; a new conversation adopts the upgraded skill without modifying an existing conversation's history.
Story quality evidence
Implementation, verification and review run after the deterministic owner approval
gate. Their authored context identifies that approval as a workflow-journal fact,
not a repository file. Worker checkouts intentionally use detached HEAD at the
pinned starting revision; local commits are exported in commits.bundle and do
not require a named branch or an upstream push.
Every verifier check is a repeatable shell assertion with exit_code=0 and
expected_exit_code=0 (the default). A negative CLI behavior check wraps the
application invocation in an assertion of its exit code and output; the assertion
itself must succeed. The deterministic quality gate repeats each assertion in
the same disposable checkout and retains its actual exit and bounded output.
It never accepts a failed suite because the model declared that failure expected.
A mismatch, unexecuted check, unresolved
acceptance finding or malformed report fails the quality gate and prevents code
publication. Non-blocking observations belong in optional string notes.
One worker per run and bounded repairs
A worker belongs to a run, and a terminal outcome releases it after artifact collection/checkpoint. An unfinished Prism Story does not by itself retain a VM. Coding workflows for Codex, Claude and OpenCode keep implementation, verification and review in the same checkout for up to three repair iterations. Failed test or review reports feed the next implementation session without a new run, VM or plan-approval replay. The final quality gate still rejects an exhausted cycle. An intentional new attempt with the same Story, originating chat/topic, task, repository revision, workflow ID, worker, image and agent reuses the exact approved plan text from its durable Human journal. It starts at the authored implementation/repair/quality subtree rather than regenerating a plan. A new idempotency key and repaired workflow pack do not invalidate that decision; changed execution scope or later owner rejection/amendment requires approval. Approval snapshots travel only in private worker manifests and are omitted from public status. ACP permission and provider sign-in requests remain independent. Infrastructure/process failures, explicit cancellation and the original deadline remain terminal; they are not indefinite VM retention or blind workflow replay.
Runner and its separate RPC processes open the same SQLite journal. Write
transactions reserve the writer before reading the update snapshot
(BEGIN IMMEDIATE), avoiding a deferred-transaction SQLITE_BUSY_SNAPSHOT (517).
The busy timeout is initialized per connection before normal database setup.
execution_diagnostics reads the checksum-verified Runner/Callee log for every
unsuccessful terminal state, including expired and canceled runs. For example,
an approval wait that reaches the original run deadline reports
context deadline exceeded rather than only callee_event_processing_failed.
Logs produced before Runner recorded the underlying error cannot reconstruct
that missing cause retrospectively.
If Claude clears every native OAuth token after an authentication rejection,
the stopped-worker checkpoint retains its account/organization binding and
marks the issuer cache as requiring sign-in. The issuer preserves the previous
encrypted bytes, permits cleanup of that exact worker, and does not reissue the
rejected cache. The next attempt uses the existing browser authentication Human
before allocating a VM; completing sign-in resumes it with the retained plan.
A missing, locked or partially corrupt cache still fails closed. Cleanup retries
use cleanup_error and never overwrite the recorded execution failure.
Retained input dependencies
Inputs/tooling require a consistent Broker, gateway, Runner and bootstrap set. Drain v2 runs before rolling back binaries; older builds can discard new fields when reading state. Restore a prior database only as part of an explicitly chosen consistent recovery point, accounting for all admissions after that point.
Use execution_start.inputs only with the exact source run, artifact name and
SHA256, in the original chat/topic. The source must have completed cleanup and
still be retained when admitting the dependency. Retention automatically holds
its bytes until dependent cleanup or the original dependent deadline plus five
minutes; restart does not extend this limit. A canceled admission takes no hold.
artifact_input_failed includes the concrete failed transfer prerequisite in the
terminal detail. Changed ETag/bytes, inaccessible sources, invalid ranges and
redirects stop preparation before Callee starts. A lost Runner process may resume
only its explicit input preparation checkpoint. Do not relabel a lost active
Callee process as an input checkpoint to force a replay.
The internal /worker-inputs/{run_id}/{name} route accepts only the scoped,
expiring worker token and supports byte ranges. It is not a public artifact link.
Keep the normal Broker token out of workers. Input preparation does not install
tools, run an archive's contents, or expand a managed-host approval.