Content
View differences
Updated by Jonas Jabari 13 days ago
Part 2 3 of 5 of the text transform API for the description assistant, in review order dev → AI-133 → AI-135. assistant. Depends on AI-133 only. The function layer over the data layer: the rules that decide whether an action may run, the port to the LLM, parts 1 and 2. Runs the execution that runs the LLM call outside the request cycle and writes progress to the run's event log.
Branch `implementation/ai-135-execute-text-transforms-in-a-background-job`, PR #25171 (base: the AI-133 branch; #25170 was closed automatically by GitHub during a stack reorder, nothing was merged).
## Scope
* `AI::TextTransforms::Context`: request-scoped value object for an existing work package, a new work package of a type in a project, or no context at all; resolves - Prompt assembly service: system message = fixed scaffold + `action.prompt` + the type variant and its template. Never persisted.
* `AI::TextTransforms::Availability`: `assistant` (feature flag, setting, gateway readiness), `action` (plus active, usage scope vs. type, template present for `injects_type_template`), `runnable` (the job-time re-check: assistant and active only), `actions_for`. Every "no" carries a reason: `feature_disabled`, `assistant_disabled`, `llm_unavailable`, `action_inactive`, `context_required`, `type_mismatch`, `template_missing`. when the action injects it. User message = the submitted content verbatim. Content is never interpolated into the system message.
* - Gateway port: `readiness` and port `stream(system:, user:, timeout:) user:) { |delta| }`. `NullGateway` answers The null implementation from part 2 raises "not configured" and raises on stream; the real implementation comes in AI-137. `Errors::*` with the four reasons, `Cancelled`, and a `FakeGateway` spec available". A test double that replays scripted deltas, errors and clock advances. delays.
* `AI::TextTransforms::Execution`: streams - `AI::TextTransformJob` on a dedicated queue, zero retries, hard timeout of about 180 s. Loads the run's stored `system_prompt` and `input` through run by id (job arguments carry the gateway, id only), marks it running, streams, buffers deltas and appends a `text_delta` event every ~500 ms, re-reads the run checks `cancel_requested` at each flush (`cancel_requested` ends it `cancelled`, a deleted run ends silently), fails with `timeout` beyond and aborts the 180 s budget, stream, then appends `completed` with the full text and marks the run succeeded. Errors map
- Availability re-validated at job start.
- Error taxonomy mapped to translated `error` events; events: LLM not configured or disabled, connection failed, timeout, upstream payloads never reach users or logs. error. Never raw upstream payloads.
* `AI::TextTransformJob`: glue only, dedicated queue `ai_text_transforms`, zero retries, no JobStatus row, executes as the run's user with the run id as the only argument. - Prompts and responses are not logged above debug level.
* i18n keys `ai.text_transform.errors.*`; specs for context, - Specs against the availability matrix, the execution with an injected clock (flush windows, test double: flush throttling, cancel, deletion, timeout, each error path, timeout, the full text in `completed`) and the job. `completed`.
## Notes
* No prompt assembly here: - The scaffold wording is a working state; the final prompt set is assembled at create time and stored on the run (AI-136). AI-97.
* - Capacity is controlled by the GoodJob queue pool, e.g. `OPENPROJECT_GOOD__JOB__QUEUES="ai_text_transforms:2;-ai_text_transforms:20"`. pool. `good_job_control_concurrency_with` would be is a backstop, not backpressure, and is not used. Documenting backpressure. Document the queue for operators is a follow-up.
* The budget doubles as in the gateway's transport timeout, so there is no `Timeout.timeout`. operations docs.
Branch `implementation/ai-135-execute-text-transforms-in-a-background-job`, PR #25171 (base: the AI-133 branch; #25170 was closed automatically by GitHub during a stack reorder, nothing was merged).
## Scope
* `AI::TextTransforms::Context`: request-scoped value object for an existing work package, a new work package of a type in a project, or no context at all; resolves
* `AI::TextTransforms::Availability`: `assistant` (feature flag, setting, gateway readiness), `action` (plus active, usage scope vs. type, template present for `injects_type_template`), `runnable` (the job-time re-check: assistant and active only), `actions_for`. Every "no" carries a reason: `feature_disabled`, `assistant_disabled`, `llm_unavailable`, `action_inactive`, `context_required`, `type_mismatch`, `template_missing`.
*
* `AI::TextTransforms::Execution`: streams
- Availability re-validated at job start.
- Error taxonomy mapped
* `AI::TextTransformJob`: glue only, dedicated queue `ai_text_transforms`, zero retries, no JobStatus row, executes as the run's user with the run id as the only argument.
* i18n keys `ai.text_transform.errors.*`; specs for context,
## Notes
* No prompt assembly here:
*
* The budget doubles as