Smiling cotton character from the original Cotton logo, with the background and lettering removed
Back to the hall

Cotton

Translation workflow

Turning generated translations into reviewable results.

A translation workflow for game dialogue.

2026.05.06 — 2026.05.26

Work project · translation processing, judgment and review path

  • Web appImplemented · development

Cotton vocabulary

Lens
Refinement Loop
Translation
Quality Estimation
Workflow
Job Control
Human Review
  1. prompt layers
  2. glossary
  3. generation
  4. revision
  1. judge
  2. hard gate
  3. score
  1. intake
  2. checkpoint
  3. spend control
  4. stale sweep
  1. approval
  2. correction
  3. audit trail

Prompt Layers: SYSTEM_PROMPT_L0 · buildTranslateUserMessage · L1 CONTRACT · L3 SPEAKER · L6 SOURCE · L7 INSTRUCTION · ICU PLACEHOLDER · INLINE TAG · TARGET TEXT ONLY · FORCED MAPPING · L2 GLOSSARY · speaker_keyGlossary: glossary_term · fetchGlossaryHits · canonical_form · forbidden_translations · SUBSTRING MATCH · FORCED MAPPING · FORBIDDEN ALTERNATIVE · L2 GLOSSARY · glossary_hits · glossary_complianceGeneration: AdapterRegistry · anthropic-sdk · openai-sdk · claude-local · codex-local · llm_plan · PIVOT_VIA · shouldPivot · COTTON_ADAPTER_CONCURRENCY · NODE_OPTIONS · pivot_register_carry · translation.refinedRevision: buildTranslateUserMessage · L7 INSTRUCTION · runRefinementLoop · priorAttempt · priorCritique · REJECTED BY THE JUDGE · maxTurns · willRetry · RefinementHooks · HOOK ABORT · turn_count_to_final · critique_for_retry · failingDimensions · cost_ceiling_usd · needs_reviewJudge: glossary_hits · runJudge · JUDGE_SYSTEM_PROMPT · judgeModel · TEMPERATURE 0 · parseJudgeResponse · FOUR-SECTION XML · VERBATIM CHUNKS · severity · critique_for_retry · judge_citation · missingHardGates · TRIGGER ABSENT = PASS · judge.scoredHard Gate: ICU PLACEHOLDER · INLINE TAG · FORBIDDEN ALTERNATIVE · hard_gate_fail · hardGatePassed · glossary_compliance · cps_budget_compliant · icu_validity · inline_tag_preserved · length_constraint · HARD GATE FIRST · missingHardGates · TRIGGER ABSENT = PASSScore: HARD GATE FIRST · computeVerdict · cottonEvalScore · WEIGHTED AVERAGE · tone_register · personality_consistent · emotional_state_match · cultural_marker_preserved · accuracy · pivot_register_carry · auto_approve_threshold · failingDimensions · approval_gate · judge.scoredIntake: processXlsxBytes · classifySheetName · parseGoogleSheetsUrl · source_file · content_sha256 · extractStageDirection · speaker_key · ingest_session · segment.ingested · github.push.receivedCheckpoint: translation_progress · last_completed_step · turn_translated · turn_judged · finalized · jobEntryGuard · SELECT FOR UPDATE · attempt_count · DEFAULT_MAX_RESUME_ATTEMPTS · TWO-TX SPLIT · ON CONFLICT DO NOTHINGSpend Control: llm_plan · costCap · cost_ceiling_usd · rateLimit · FailoverDispatcher · haltOnQuota · detectQuotaFromStream · quota_daily_exhausted · auth_revoked · NO AUTO-SWAP · needs_credential · POST /:id/resumeStale Sweep: COTTON_ADAPTER_CONCURRENCY · NODE_OPTIONS · attempt_count · runStaleSweep · STALE_THRESHOLD_INTERVAL · DLQ_ATTEMPT_CAP · LIMIT 200 · re_enqueued · dead_lettered · ORPHAN RE-ENQUEUE · IN-FLIGHT SET · /health/queues · maxStalledCount · needs_reviewApproval: auto_approve_threshold · ingest_session · SELECT FOR UPDATE · needs_credential · POST /:id/resume · POST /:id/approve · POST /:id/unapprove · POST /:id/reject · APPROVE_FROM · REJECT_FROM · needs_review · reviewer_id · HITL · approval_gate · translation.approved · EDIT & APPROVE · actor_idCorrection: POST /:id/correct · corrected_text · CORRECT_FROM · EDIT & APPROVE · human_corrected_segments · human-corrected-after-mt · translation.human_corrected · supersedes · NEW REVISION · IMMUTABLE HISTORYAudit Trail: turn_count_to_final · severity · judge_citation · content_sha256 · segment.ingested · github.push.received · translation.approved · human_corrected_segments · translation.human_corrected · IMMUTABLE HISTORY · event_log · appendNextEvent · ULID · AGGREGATE VERSION · translation_state_log · actor_id · llm_call_log · telemetryHook · judge.scored · translation.refined

Scores, retries and human review, each at its own boundary.

Generation and judgment share the glossary, and a hard-gate failure reported by the judge model cannot be offset by the mean score. Intermediate translation and judgment states are recorded, retry and cost limits are kept apart from exhausted credentials, and stalled jobs return to the queue or to review depending on the cause.

This work project connects translation generation, model-reported quality judgment, retries and human review in one flow.

From the start of the repository to the work confirmed during the engagement

A web app of translation editing and approval screens, an API and a job worker, running in the development environment.

Judgment flow map · from private implementation records
Cotton translation judgment · retry · recoveryThe API commits the source text and target locales to the database, then queues a job per target locale. A worker locks the row, checks the attempt, creates the draft and progress state, generates a translation with the glossary, and calls a judge model separately with the same glossary. The translation (Tx1) and the judgment with its evidence (Tx2) are recorded in separate transactions. Code aggregates the judgment so a hard-gate failure is not offset by the mean: a pass is finalized, a failure gets another turn, the turn or cost limit sends it to human review, and a quota or auth signal stops spending and waits for a credential check. A stale sweep looks at up to 200 drafts older than 10 minutes: one with attempts left but missing from the queue is re-queued, and one with attempts used up goes to review or the credential check depending on whether a result exists.Source · target localesStored segmentsHuman reviewneeds_review · not approvalCredential checkneeds_credentialAPI · job registrationQueue after commitJob queueBullMQ · job IDJob entryRow lock · attempt checkGlossary · contextWorkspace glossaryTranslation turnGenerate · model callJudge model call · parseVerdict rulesHard gate · weighted meanRetry · spendingTurns · cost · quotaTranslation stateDraft · progressTranslation · judgment rowsState · call logFinalizationFinal transactionQueue checkCompare queue stateStale sweepDrafts over 10 min · max 200Input · queueGeneration · judgmentState · acceptanceRecovery · human decision1source2 after commit · Queue after commit → BullMQ · job ID23 deliver job · BullMQ · job ID → Row lock · attempt check34progress row5attempt allowed1glossary2 same glossary · Workspace glossary → Judge model call · parse23Tx1 · translation4Tx2 · judgment5 scores · gates · Judge model call · parse → Hard gate · weighted mean56verdict7pass8final record1verdict2next turn3limit · abort4quota · auth5manual resume1stale draft2below limit3re-queue4 result exists · Drafts over 10 min · max 200 → Human review45no result

Registration → entry

Queued after the database commit; a worker locks the row, checks the attempt and creates the draft.

  1. Source · target localesData: sourceQueue after commitAPI · job registration

    Source segments and the project’s target locales are stored in the database. Customer data is not shown.

  2. Queue after commitAPI · job registrationData: after commitBullMQ · job IDJob queue

    After the database commit, a job with a request-scoped ID is queued per active target locale. If queueing fails, only a warning is logged and the commit is not rolled back (no atomic outbox), which is why the stale sweep exists.

  3. BullMQ · job IDJob queueRun · judgment: deliver jobRow lock · attempt checkJob entry

    BullMQ hands the job to a worker asynchronously.

  4. Row lock · attempt checkJob entryData: progress rowDraft · progressTranslation state

    Inside a transaction the progress row is locked and a draft translation is created. Automatic retries of finalized or credential-paused jobs are skipped; manual requests are handled separately.

  5. Row lock · attempt checkJob entryRun · judgment: attempt allowedGenerate · model callTranslation turn

    The attempt count goes up by one within its limit, then the translation turn starts.

Generate → judge → finalize

Generation and judgment use the same glossary; the translation and the judgment are recorded separately, and code aggregates the judgment before finalizing.

  1. Workspace glossaryGlossary · contextData: glossaryGenerate · model callTranslation turn

    The workspace glossary and context are looked up and passed to generation.

  2. Workspace glossaryGlossary · contextData: same glossaryJudge model call · parseTranslation turn

    The judgment gets the same glossary constraints.

  3. Generate · model callTranslation turnData: Tx1 · translationTranslation · judgment rowsTranslation state

    The model is called directly or through an en-US pivot, and the call, event and progress are written in one transaction. If the job runs again after a crash, the translation call and its cost can repeat.

  4. Judge model call · parseTranslation turnData: Tx2 · judgmentTranslation · judgment rowsTranslation state

    Judge scores and evidence are inserted once per translation and turn; a duplicate finds the existing row, and citations are written only on the first insert. Not the same transaction as the translation.

  5. Judge model call · parseTranslation turnData: scores · gatesHard gate · weighted meanVerdict rules

    The judge model’s answer is parsed into per-dimension scores and gate results. Whether the glossary passed is what the judge model reports.

  6. Hard gate · weighted meanVerdict rulesRun · judgment: verdictTurns · cost · quotaRetry · spending

    Any reported hard-gate failure means a zero and a fail; only then are the weighted mean and per-dimension thresholds applied. The overall threshold of 8.0 is a configuration default, not an observed translation quality.

  7. Turns · cost · quotaRetry · spendingRun · judgment: passFinal transactionFinalization

    A pass is finalized as translated.

  8. Final transactionFinalizationData: final recordState · call logTranslation state

    Candidate, state, score and turns are finalized, and progress finalized plus the state log are written in one transaction. A translation-complete event is emitted only for translated.

Retry · spending limits

No pass means another turn; a limit means human review; quota or auth stops spending and waits for an operator to resume.

  1. Hard gate · weighted meanVerdict rulesRun · judgment: verdictTurns · cost · quotaRetry · spending

    Any reported hard-gate failure means a zero and a fail; only then are the weighted mean and per-dimension thresholds applied. The overall threshold of 8.0 is a configuration default, not an observed translation quality.

  2. Turns · cost · quotaRetry · spendingRun · judgment: next turnGenerate · model callTranslation turn

    A translation that does not pass is generated again with the previous translation and critique. Even a hard-gate failure can be retried first rather than going straight to a person.

  3. Turns · cost · quotaRetry · spendingRun · judgment: limit · abortHuman review

    When the turns run out or the cost ceiling is reached, the job is finalized as needs_review. Finalized means processing ended, not that a person approved it.

  4. Turns · cost · quotaRetry · spendingFailure · limit: quota · authCredential check

    A quota or auth signal stops spending and sets needs_credential. There is no automatic switch to another account or provider. Transport errors without a halt signal go back to BullMQ retry.

  5. Credential checkRun · judgment: manual resumeBullMQ · job IDJob queue

    An operator who has checked the credentials restarts explicitly.

Stale job recovery

Old drafts are split by reason: missing from the queue means re-queue, attempts used up means a human decision.

  1. Draft · progressTranslation stateObservation · report: stale draftDrafts over 10 min · max 200Stale sweep

    Up to 200 candidates that have been drafts for over 10 minutes are read, oldest first. The query takes no lock and updates and logs run as separate statements, so this is not described as a race-free batch.

  2. Drafts over 10 min · max 200Stale sweepRun · judgment: below limitCompare queue stateQueue check

    With fewer attempts than the limit (3), the job is compared with the active, waiting, delayed and prioritized queue states. In code the attempts-exhausted branch comes before this queue check.

  3. Compare queue stateQueue checkRun · judgment: re-queueBullMQ · job IDJob queue

    If the job is not in the queue, it is queued again. The API uses IDs with a request suffix while the sweep looks for the segment and locale ID, so it is not described as matching every in-flight request.

  4. Drafts over 10 min · max 200Stale sweepRun · judgment: result existsHuman review

    With all 3 attempts used and a translation result present, the job goes to needs_review.

  5. Drafts over 10 min · max 200Stale sweepRun · judgment: no resultCredential check

    With all 3 attempts used and no result, the job goes to needs_credential.

Every connection at the same weight. Pick a flow to highlight only its path, with numbered steps.

  • Solid · direction of input, results and records
  • Dashed · run, judgment, request
  • Long dots · observation and reporting, apart from judgment
  • Short dashes · failure, stop, limit
  • Heavy black line · the selected flow; numbers give the order
  • Box · code or state boundary, named in its header
  • Inner chip · a component of that boundary
  • Dotted box · outside that boundary
  • Orange outline · where a model decides
  • Generation · spending · finalization
  • Entry · verdict rules · sweep
  • Queue · state · records
  • Registration · queue check

Drawn from private implementation and development records. Customer data and code paths are not shown.

One sweep
May 26, 2026, one development-environment sweep cycle: 200 checked, 106 state transitions, 2 re-queued. A state transition is not a successful translation, and the split of the 106 by destination state is not recorded.
Why not exactly once
Records are written without duplicates, but if a job runs again after a crash, model calls and their cost can repeat. The concurrency limit is per process, not cluster-wide.
Scope of the verdict
Keeping a mean from covering a glossary failure is an aggregation rule over reported gates. It does not prove that every banned term is found or that quality is native-level.