# YETI³: asynchronous execution and staged migration

## Contract for current and future development

BUS is the shared Rust/RabbitMQ queue server. Applications retain their business databases, authorization, original IDs and history. A queue transports a reference to an existing authorized task; it must not transport passwords, private keys, full chat prompts or payment details.

Python SDK exposes `yeti3_bus.async_client.AsyncClient` and the constructor-discoverable `network.bus.async` capability. Use its async context manager and await `submit`, `job`, `wait`, `dispatch`, `claim`, `heartbeat` and `finish`. `wait` cancellation stops the local observer; it does not prove cancellation of a remote provider job. Persist the submitted job ID before polling. An explicit stable request key prevents replay of a paid operation. Synchronous compatibility APIs remain available during the staged migration.

Rust uses async networking, PostgreSQL transactions and publisher confirmations. CPU-heavy computation runs outside the async reactor. Existing SHA256 formats and immutable histories remain unchanged.

## Processing priority

| Stage | Work | Required proof before production |
|---|---|---|
| P0 | Durable queues; payments and GPU orchestration; blockchain jobs; auth invariants | Duplicate and conflicting requests, access scope, lease/fencing, crash recovery, no duplicate money/provider actions |
| P1 | 1C, SED, banks, CRM exchange, Telegram, reporting, indexing, media and SEO | Provider timeouts, limits, retries, cancellation, migration and reconciliation |
| P2 | Constructor and business applications | Immediate response, saved job ID, progress, resumable reload, mobile and role checks |
| P3 | Remaining module candidates | Contract, relevant automated tests, docs, deployed user path |

The source audit is an inventory, not a performance benchmark or provider acceptance test. Review candidates include deployment/test scripts that should stay synchronous. Aliased imports are resolved for common Python blocking I/O. Rust candidates require review of reactor scope and blocking boundaries. Do not mechanically change every function into `async`.

## External executor protocol (0.3.0)

1. POST `/v1/queues/{network-ai|codex}/tasks`: original source_task, owner_id, project_id, request_sha256, optional decision_sha256 and immutable request_key.
2. POST `/v1/queues/{lane}/claim`: worker identity. Per-client RabbitMQ queues carry dispatch IDs only.
3. Save claim token and generation; heartbeat at most every 20 seconds. The lease lasts 60 seconds.
4. Recheck the original authorized MySQL task and decision hash before execution. Persist provider inference/quote keys before a paid action.
5. Finish with the exact token/generation and bounded result. An identical completion replay is accepted; a conflicting or obsolete completion is rejected.
6. An expired lease becomes `uncertain` and enters the DLQ. No automatic paid retry. Reconcile persistent provider and step state before enqueueing a new dispatch.

BUS is not permission to skip approvals. Cancelling a pending dispatch does not destroy a running GPU instance. Provider teardown needs separate confirmation from the provider API. Apply `deploy/002_dispatch.sql` before starting runtime 0.3.0. It is additive; old jobs and immutable audit history are retained.

## Resource control and horizontal scaling

Hard ceiling: **three service instances**. Never increase this limit from an application request. External execution admits at most three live leases globally, with a PostgreSQL serialization lock; this is a separate concurrency limit, not proof that three containers are deployed.

Before claims, BUS measures Linux host available memory and load. Less than 512 MiB available or load above 1.5 per host CPU defers execution. Missing measurements defer execution. Scaling must additionally use the host capacity admission check for disk, cgroups, CPU and memory. More copies on an exhausted host do not create capacity. Preserve shared volumes, queues, user data, VPNs and other projects.

Three replicas can share PostgreSQL/RabbitMQ; SQL ownership and fencing are authoritative. Do not use in-memory ownership as a distributed lock. Stop scaling down until active work has completed or been reconciled. Current public domain is a status/landing surface; queue APIs are private.

## Constructor integration contract

Every long operation must expose: operation ID, required capability, permission, input schema, idempotency policy, queue lane, progress schema, cancellation semantics, retry/reconciliation policy and output reference. UI renders queued/running/completed/failed/cancelled/uncertain distinctly; a spinner is not completion. Persist operation IDs on reload; polling pauses in hidden tabs and resumes on visibility. SSE/WebSocket subscriptions must use authenticated tenant scopes.

Constructor integration remains a later staged release; this document does not claim existing constructor components have all been migrated.

## Inventory and regression check

Run `tools/sdk/audit_async.py --root ENGINE --root GOCHAIN --root BUS --output audit.json`. Add `--baseline previous-audit.json` to reject newly introduced known blocking calls inside Python async functions. Run `python -m unittest discover -s tests/sdk`; run Rust tests and the SDK client tests. Each actual migration needs a meaningful runtime integration test in addition to static checks.

Roll out one module at a time: immutable source revision → image/package → migration → readiness → authenticated functional path → acceptance record. Keep the previous image and data rollback boundary until checks pass.

## Initial audit — 13 October 2026

Snapshot: Engine 2,311 source files / 168 manifests; GoChain 1,180 source files / 4 manifests; BUS 60 source files / 3 manifests. Initial 2,417 candidates (2,637 after import-alias resolution) include deployment and test utilities. A completed inventory does not mean all candidates were migrated. Runtime P0 candidates are reviewed first; legacy copies are not deployed automatically.
