olenji.

Published application 1.2.1 · Versioned documentation · Markdown source

Automatic translation recovery

Automatic translation follows the session's explicit auto_translate setting, or the workspace default when the session has no override. It operates on committed source revisions and is independent of audio capture. The CLI, MCP, API, browser and terminal read the same persisted result and recovery state.

What continues automatically

A committed turn or source correction schedules a coalesced translation pass. The controller writes a pending marker before trying the ordinary job executor. If the executor is full, the marker retains ownership and admission is retried without requiring another utterance or user action. Capture does not fail merely because translation cannot currently obtain a job slot.

Retryable provider failures receive delays of 30, 60 and 120 seconds, giving an initial attempt and at most three automatic retries. An attempt is one translation pass, which can contain several bounded provider requests. A successful pass resets the consecutive failure budget, including when a later coalesced pass starts within the same job. The controller yields the executor after four successful coalesced passes if more source has arrived.

HTTP 408, 429 and 5xx errors, connection failures and request deadlines are retryable. Other errors retain the provider's existing typed retryability; configuration, privacy-policy and authentication failures normally require a repair. The four-attempt limit also applies to an interrupted attempt resumed after service restart. It is a pass-attempt limit, not a promise of four total HTTP requests for a long transcript.

The retry deadline is persisted as an explicit UTC timestamp. Restarting Olenji resumes owned pending, running and retry_wait records after rereading current preferences and privacy policy. A future deadline is preserved. Startup does not start audio capture or sweep unmarked historical sessions into a provider. Completed, disabled, cancelled and blocked records do not automatically replay.

Reading state

session.metadata.translation_automation is service-owned metadata. It appears in session reads and JSON exports, but the public metadata-write schema rejects attempts to set it directly. A waiting example is:

{
  "state": "retry_wait",
  "attempts": 1,
  "max_attempts": 4,
  "next_retry_at": "2026-10-25T16:00:30.000+00:00",
  "reason": "TRANSLATION_PROVIDER_ERROR",
  "job_id": "job_example"
}
State Meaning
pending Work is owned and waiting for admission or another coalesced pass.
running An automatic pass is processing committed source.
retry_wait A retryable failure is waiting until next_retry_at.
blocked A nonretryable failure or exhausted budget requires a relevant explicit repair.
cancelled This automatic generation was explicitly cancelled.
disabled Automatic translation has been disabled for this session.
idle The scheduled work has completed; further source can schedule more.

attempts counts consecutive pass attempts in the current failure chain; successful completion resets it to zero. reason is a stable code, including RETRY_EXHAUSTED when the budget is exhausted. The existing translation_error contains the explanatory error. job_id identifies the latest admitted owner when one exists; a marker awaiting its first admission can have null here. Historical job records retain their actual completed/failed/cancelled result.

Polling for capacity and coalescing source commits do not repeatedly emit the same visible marker. The browser and terminal display these states without creating their own retry timers.

Repair and cancellation

An explicit edit of auto_translate, translation_language, translation_provider, translation_model or local_only reconsiders owned work with a fresh retry budget. Selecting auto_translate: true again is a deliberate retry request even if the stored value is already true. Tags, retention and presentation edits do not reset a blocked budget.

Provider repairs and relevant workspace-setting edits wake previously owned unfinished/blocked work. A provider-specific repair matches the session's explicit provider, the applicable workspace default, or an implicit provider selection that the repair can affect. It does not replay an unrelated explicit provider, completed session or legacy unmarked session. A disabled session continues to require explicit enabling.

An ordinary manual translate request remains independent. It can translate committed source after a provider repair without changing the automatic retry budget. To resume blocked future automatic processing, explicitly reenable automatic translation or edit the relevant provider/preferences.

Cancelling an active or queued automatic job ends that generation. Cancelling the latest failed automatic job can also cancel the deferred retry it still owns, including a durable record outside the bounded in-memory controller. The historical failed job remains failed. Cancelling an older failed job does not cancel a newer admitted retry with a different job ID.

An admission reservation temporarily cannot know whether the executor accepted a newer job. It retains one cancellation intent for the previous owner and applies that intent if admission rejects. A successfully accepted newer job has its own owner. This prevents a full executor from reviving a cancelled retry without allowing an old job ID to cancel new work.

Cancellation ends work already coalesced into that generation. A source or explicit preference request after cancellation completes can create another generation. To prevent future automatic work, set auto_translate to false. Disabling is checked before provider resolution and before every further batch. An already transmitted provider request cannot be retracted; its valid returned track can still be saved. No subsequent batch starts while disabled.

Ownership and bounds

The controller owns one sleeping thread per runtime and at most 128 small session entries. The database owns overflow. Startup, repair and overflow scans use stable ID pages of 128 metadata rows; they do not materialize a workspace's transcript text. Unmarked and idle rows are skipped before full session/statistics or configuration reads. Default provider lookup is sampled once per candidate page and checked again at actual admission.

Job admission occurs outside the controller's condition lock. Each reservation has a private generation token, so a delayed earlier submission return cannot replace a later retry's job ID. Queued-cancellation callbacks, provider results and cleanup act only on their owning entry. Repair pages recheck current durable eligibility before admission; a page fetched before cancellation cannot revive that cancelled record. Metadata is rechecked under the controller's ordering lock so a stale source-triggered read cannot republish an older disabled state after a newer explicit enable.

Shutdown first stops and joins the controller, then drains owned job callbacks before closing SQLite. Shutdown interruption retains pending ownership; explicit cancellation retains cancellation. No retry timer, provider client or executor worker remains owned by the recovery controller after runtime closure.

The translator still serializes manual and automatic passes per session. It rechecks source revisions when saving, preserves human-authored tracks, checks local-only policy for each provider batch and does not republish an obsolete selected target. Provider responses never become new source transcript text.

Verification and limits

tests/test_translation_recovery.py owns the retry, capacity, restart and cancellation behavior. Its controlled HTTP providers return literal known Turkish/German content, and the assertions check actual source revisions and saved tracks. Concurrent operations use events at request, snapshot, lock, admission and submission-return boundaries. Fixture teardown releases blocked operations, drains jobs and checks that the controller thread has stopped. The adjacent lifecycle tests retain manual target ownership, automatic retarget and agent-appended source coverage.

The review record records executed tests, preceding-build failures and isolated fault variants. A separate installed-package acceptance harness, tests/smoke_translation_recovery.py, uses a real loopback HTTP provider and the production retry delay to check service restart outside the source checkout. Its actual execution belongs to the current validation index, not to the unit-test result.

Recovery begins after the pending marker is persisted. Source commit and automatic scheduling are not one transaction: a process killed in that small interval can leave newly committed source requiring an explicit enable or translation request. Recovery does not claim exactly-once provider delivery: an interrupted request may have reached a remote provider before Olenji could persist its response. Successful revision-bound tracks are not retransmitted on a later pass.

The translator still snapshots all committed source and existing tracks per pass. This controller bounds scheduling memory, not that separate long-session translation snapshot. Provider retry hints such as Retry-After are not yet used; the documented bounded local backoff remains the policy. Paid-provider availability, translation quality and hardware capture are separate acceptance gates. No model, native runtime or network access is silently installed or enabled by recovery.