Article · Engineering
ai-worker, Part 2: Architecture and Execution Boundaries
Inside the current ai-worker: a shared scheduler, ordered provider adapters, a protected web control plane, isolated QEMU execution, snapshot-bound evidence, and delivery cleanup.
Read in another language: RUai-worker is a local Python controller connecting Forgejo tasks with coding agents. The host stores workflow decisions and controls publication. QEMU guests run repository tooling and native provider CLIs, returning code and structured evidence. An optional local model server uses the host GPU while its tool work still runs in a guest.
Part 1 describes the workflow and shows the web
Overview. This article describes the implementation
at commit 824c821, current as of November 10, 2026. New sequential plans,
legacy parallel features, and directly assigned issues retain their distinct
authorization and delivery policies.
Components and authority
The controller discovers work, verifies current authorization, claims a task,
selects a provider, runs a phase, and publishes accepted results. The Forgejo
adapter uses authenticated tea operations. Separate non-admin planner, worker,
and reviewer identities keep planning and merge coordination, implementation,
and formal review attributable to their respective roles.
SQLite stores tasks, attempts, provider observations, and claims. A bare Git cache holds objects and slot-specific references. Runtime modules move bundles, prepare guests, and record completion. Separate modules handle evidence contracts, review repairs, reference images, metrics, repository access, and cleanup.
The web service reads these same records and invokes bounded scheduler or systemd actions. It does not introduce another workflow engine. Polling, scheduling, CI dispatch, publication, merges, and cleanup use ordinary Python; only planning, implementation, review, and applicable validation jobs use inference.
A shared scheduler dispatches available slots
One user-systemd scheduler coordinates a shared three-minute delay between work checks. Per-slot services execute jobs; retired independent ten-minute slot timers are no longer the scheduling mechanism. A local discovery lock serializes selection and claiming, then releases before the selected model phase runs.
The scheduler reloads configuration on local ticks, observes service and slot availability, and records check receipts and deadlines. Start now can request an immediate check through that same mechanism. A completed job that made progress can request a follow-up check without waiting for the normal delay.
The shares setting allows one, two, or three execution slots. New plans advance
one milestone at a time per parent, while different parents can use multiple
implementation slots. Planning and review may run alongside them; reviewed-PR
repairs have a specific same-parent exception. A PR waiting for review or CI
does not need to occupy an implementation guest indefinitely.
SQLite WAL mode, atomic task claims, and compare-and-update writes prevent stale discovery from overwriting another slot’s progress. Kernel slot locks identify live claims and release on process death. A maintenance gate excludes provisioning from active jobs; Git-cache and feature locks protect short mutations. This is local coordination for one installation, not a distributed lease system.
Providers are selected in configured order
The ordered providers list controls activation and priority, initially
codex, claude, and llama. The local entry requires a configured server;
optional providers require explicit activation and their own admission checks.
Each phase has provider-specific model and effort settings, plus an On/Off switch. Disabled combinations are skipped before quota checks. The controller walks the configured list and selects the first eligible provider for the phase. The current policy does not rank providers by reset-weighted headroom scores.
For providers with measured windows, every applicable window must remain strictly
above its reserve after the in-flight penalty. The default penalty is five
percentage points for each other running job on that provider. reserve_percent
defaults to ten; weekly_reserve_percent can independently protect weekly and
model-specific weekly limits. Omitting the weekly setting inherits the ordinary
reserve. Equality defers work.
Reset timestamps support validity, caching, and displayed quota. Invalid or blocked readings make the provider unavailable; failures back off. An empty list suspends inference while reconciliation continues. Configuration changes apply to subsequent jobs.
Eligibility means different things for each provider
The common adapter contract does not make all upstream signals equivalent:
| Provider | Admission signal and constraint |
|---|---|
| Codex | Native CLI rate-limit metadata; defer when paid credits are available |
| Claude | Read-only OAuth usage windows; defer when paid extra usage is enabled |
| Kimi Code | Verify the selected login and model; membership has no read-only quota API |
| DeepSeek Harness | Explicitly enabled paid API key and available account balance |
| OpenCode Go | Usage windows, verified billing guard, and bound live acceptance evidence |
| Local llama.cpp | Configured server, eligible simple work, resource checks, and a bounded local attempt |
Codex metadata is read without creating a model thread or turn. Claude’s internal usage endpoint is isolated behind its adapter because it is not a stable public contract. Kimi’s synthetic headroom means login verification succeeded; exhaustion is detected from CLI rate-limit errors during execution. DeepSeek’s synthetic 100% means funds are available, not that a subscription quota is untouched. Listing DeepSeek explicitly authorizes spending API balance.
OpenCode Go is opt-in. It requires a worker-owned Console login, an applicable zero-dollar All-spend Console budget, and live acceptance evidence bound to the account, workspace, CLI, guest layer, adapter, and result schema. The adapter rechecks the budget and usage windows immediately before a new phase. An absent, unreadable, or unsuitable guard makes it unavailable. These are worker admission checks, not general promises about provider billing.
Quota failures preserve work and refund the model attempt. The next poll can choose another configured provider. Quota percentages and balance signals are scheduling inputs rather than comparable token capacities.
Fresh sessions use explicit continuation artifacts
Every remote phase starts a fresh CLI session with restricted inherited configuration and integrations. The approved scope, source, prior findings, validation evidence, and retained Git work provide continuity across providers. Conversation histories are not translated between them.
Planning, review, and implementation have separate settings. A validation job
uses implementation settings. Milestones marked simple, and eligible minor
review fixes, can layer simple_implementation overrides over implementation
defaults. Recorded job settings remain available after later edits.
The llama.cpp route permits one local try per eligible simple implementation or bounded-fix snapshot, with a diff budget and fallback. Planning, review, validation, and conflict repair never use it. A dedicated restricted forwarder connects the guest to the host GPU model server, an explicit exception to public-service-only egress.
Files and Git objects cross the execution boundary
The host pins head and base in the Git cache; review fetches must match Forgejo. Bundles, prompts, context, settings, and the result schema enter the guest without repository-server credentials.
Writer-supplied issue images are fetched by the host through bounded, signature-checked attachment handling. Eligible image metadata participates in the requirements fingerprint; rejected or unavailable assets are reported in context. The guest receives files rather than Forgejo credentials.
Implementation checks out its managed branch; planning and review begin at the supplied detached head. Applicable dependency setup runs deterministically before provider credentials are injected. Retained work can be refreshed against pinned base changes through ordinary merge commits and repository hooks before inference.
Results, diagnostics, and implementation bundles return to the host. Before an ordinary push, the cache imports the result and verifies expected ancestry. The remote branch head is checked after publication, including after a lost response. Current authorization, scope, and snapshots are checked again before publication or a protected merge.
Validation is a structured execution contract
Results contain completion state, blockers, questions, findings, criterion evidence, validation attempts, and scope or quota signals. Planning adds bounded milestone specifications. The host validates the schema and applies acceptance rules rather than trusting a success paragraph.
Validation requirements specify exact commands, stable check IDs, required snapshots, measurement names and scope, and separate evidence and passing obligations. Receipts bind baseline or head measurements to full commit hashes. Missing, stale, or mismatched evidence cannot authorize publication. An advisory threshold still requires complete measurements without assertion or environment failure when execution evidence is mandatory.
Ordinary independent review uses source inspection and does not run validation.
New sequential final PRs rely on accepted milestone evidence and applicable final
CI. Retained legacy parallel features still use separate final review and combined
validation receipts. The worker repository’s dispatch workflow publishes pending
and terminal CI / test (dispatch) statuses using its ephemeral job token,
including failures during setup. Provider credentials are not used by CI.
Guest isolation includes credential lifecycle
QEMU/KVM boots an Ubuntu guest from a qcow2 overlay backed by a provisioned image. The guest has its own Docker daemon and resource controllers. Host source directories and the host Docker socket are not mounted. Browser and dependency cache layers can be added without rewriting backing images used by retained work.
Restricted QEMU networking routes ordinary guest egress through a host-enforced HTTP proxy. It admits allowlisted public destinations on ports 80 and 443, validates resolved addresses, rejects private destinations, and tunnels TLS without interception. The optional local-model forwarder is separately restricted. Guest SSH uses a loopback-only port and a pinned guest host key.
Forgejo credentials remain on the host. The selected provider credential is injected into a temporary tmpfs home after setup and removed during teardown. Provider-specific isolation includes selected Kimi credential bundles and OpenCode access tokens without its refresh credential. Native refreshes are adopted back only under guarded identity and unchanged-host checks.
DeepSeek Harness starts from a private directory with repository workspace settings pinned explicitly. Repository environment files are not loaded into the Harness launcher. A native no-inference startup probe checks its permission services before execution; probe failure defers work without consuming an attempt.
The selected credential remains present during execution, so this installation targets trusted project repositories. Isolation limits access to infrastructure; it does not make hostile repository code safe to run with provider credentials.
The web API exposes bounded operations
The bundled web UI uses a loopback-only Python service with versioned session, status, task, log, settings, capability, and action endpoints. Private-network access can sit behind an authenticated HTTPS reverse proxy; it is not intended as an unauthenticated public control surface.
Mutations validate Host, permitted Origin when present, and a double-submit CSRF token. Settings saves use strict types, bounds, a revision check, a shared lock, and an atomic private-file replacement. Provider controls come from capability metadata. Unsupported installation and identity fields remain read-only.
Start, pause, resume, and confirmed slot-stop actions reuse scheduler and systemd controls. Provisioning, credential management, updates, and guest validation remain outside the web UI. Status and task refreshes make no upstream provider or Forgejo call. Recorded quotas and resource samples are observations; stale data does not grant permission to start a job.
Repository access operations use a dedicated nonblocking lock and recheck the settings revision before remote inspection. Check access reads permissions; Repair access grants missing non-admin Write access and verifies each bot login. Saving newly added repositories performs that provisioning for those entries only. Failures preserve saved settings and return sanitized warnings. Settings reads show revision-bound cached results or Unknown without remote calls. Repository removal does not revoke access; account creation and role changes remain operator tasks.
Recovery ends with confirmed-delivery cleanup
Private job files retain prompts, results, logs, bundles, and overlays while work is unfinished. Receipts bind completion to phase, head, base, and scope, allowing publication recovery without another model call. Unchanged inputs can resume retained guest work; changed bases preserve earlier work and can carry exported commits forward.
Bounded implementation and review-repair episodes preserve their budgets across polling and restarts. Evidence-only corrections can reuse a commit, but disputed findings require fresh independent review. Ownership markers and saved creation intent reconcile ambiguous remote requests before another creation attempt.
After confirmed delivery, resumable cleanup removes feature-local guests, bundles, logs, screenshots, and retired jobs. SQLite keeps results, merge evidence, and a cleanup inventory; remote discussion and uploaded evidence remain available. Shared caches and images, active claims, unrelated jobs, and pending publication or recovery are protected. Cleanup failures do not rerun delivered implementation.
Return to Part 1: From Issue to Reviewed Merge for the resulting issue, approval, review, and delivery behavior.
