Automatic translation — no human review gate — decision record
Deliverable for ADO PBI #1455 — "Make translation fully automatic; remove the human review gate." This is the durable record of the product pivot the implementation executed against.
Scope of this doc: the decision + the trade-offs behind it, and a precise account of what changed in the code and what was deliberately preserved. The code change shipped with this PBI; this record is its rationale and its boundary (what is out of scope / future).
Date: 2026-06-25.
It supersedes a prior product invariant. It retires the "apprenticeship, not a batch job" / per-batch human approval gate that
spec.mdanduser-stories.mdused to describe, and subsumes the auto-advance work. It also retires the premise of Feature #1365 that "the human-in-the-loop quality gate is the product's reason to exist."
0. TL;DR — the decisions
| # | Question | Decision | Consequence |
|---|---|---|---|
| D1 | Is there a human review/approval gate? | No. Kenly is a fully automatic translation service. Upload → upfront cost estimate → commit/pay → the whole book translates itself → read/export. | The per-page approve / reject / edit and the source-language confirm/correct steps are gone. Processed pages are auto-approved by the system. |
| D2 | How does the batch ladder escalate? | Automatically. Still calibrate on one page first, but each completed batch auto-confirms (increments ApprovalCount, widening the next batch) and the next batch is enqueued by IBatchAutoAdvancer (on the JobProcessor) with a Worker backstop. | Calibrating → Processing → Complete with auto-confirm; no human action between batches. BatchPolicy (1 → 5 → 25 → 100 → rest) is unchanged. |
| D3 | What can stop an in-flight book? | Only affordability. When the next batch would exceed the wallet balance, processing pauses cleanly (no error) and shows the "Add credit" CTA (#1396). A top-up resumes it. Failed pages are recovered by explicit retry. | The pre-commit cost estimate (#1375) + the wallet are the sole gate. Retry is the one remaining explicit control. |
| D4 | What does the reading screen do? | Read-only. Page through the translation (original scan beside the English), see per-page status, export. No decision buttons. | The completion notification fires once when the whole book finishes ("Translation ready"), not per batch. |
1. The decision
Remove the human review gate. Kenly is a fully automatic translation service with no human-in-the-loop review. A user uploads a book, sees an upfront cost estimate, commits/pays, and the entire book translates automatically with zero per-page interaction; they then read and export the finished translation. Like it or leave.
This is a product call by the product owner, not an engineering convenience:
- The decision burden was too high. Asking a reader to approve/reject/correct every page — for a book that can run to hundreds or a thousand pages — is a worse experience than a finished translation delivered for a known upfront price. The gate that was framed as "the product's reason to exist" turned out to be the product's biggest friction.
- The tech for meaningful per-page human intervention isn't the product. A reader correcting raw machine-translation output, block by block, is neither what most users want nor where the quality leverage is (that is in the OCR/translation models and an optional LLM polish pass — see
spec.md §8, M3). Surfacing a correct/incorrect decision the user is poorly placed to make added burden without commensurate quality. - An automatic service is a cleaner promise. "Upload, pay a known price, get the whole book" is a product anyone can understand; "apprentice the machine page by page" is not.
2. D1 — No human review/approval gate
Recommendation. Removed. The page lifecycle is now Pending → Processing → Approved, where Approved is reached by the system auto-approving a cleanly-processed page — there is no human approve / reject / edit-before-approve, and no confirm/correct of the detected source language.
Options considered.
| Option | Verdict |
|---|---|
| (a) Remove the gate entirely — fully automatic | ✅ Chosen. Matches the PO's product call; lowest friction; a clear upfront-price promise. |
| (b) Keep the gate but default it to auto-approve, opt-in to review | ❌ Carries the entire review UI + endpoints as dead weight for a path nobody is meant to take; invites drift and confusion about the "real" product. |
| (c) Keep per-page review, soften it (batch-approve) | ❌ Still asks the user to make per-book decisions; does not solve the decision-burden problem the PO raised. |
Trade-offs. We give up the (theoretical) per-page human quality safety net. We accept this: the quality lever is the models + an optional polish pass, not a reader's block-by-block edits. The AwaitingReview / AwaitingApproval enum members were originally retained for back-compat with any project persisted under the old model — the automatic flow never entered them.
Update (2026-06-27). Those vestigial states were removed entirely (along with
PageStatus.Rejected):ProjectStatusis nowCreated · Calibrating · Processing · Complete · Failed,PageStatusisPending · Processing · Approved · Failed, and theAwaitingReviewqueue kind / lifecycle bucket / status filter are gone. The product owner directed the full removal once the DB held no pre-pivot records. Cancellation (Cancelling/Cancelled) and a possible explicitPausedstate are tracked separately (see §7).
What changed in code. The approve / reject / edit-translation / confirm-source-language endpoints and their state-machine transitions were removed; ProcessNextBatchAsync now auto-confirms a completed batch instead of parking pages for review.
3. D2 — Automatic batch escalation (calibrate first, then auto-advance)
Recommendation. The batch ladder survives; only its trigger changed. The flow still starts narrow — a single calibration page — and widens 1 → 5 → 25 → 100 → rest. What changed: each completed batch auto-confirms (increments ApprovalCount, which BatchPolicy.NextBatchSize reads to widen the next batch) and the next batch is enqueued automatically by IBatchAutoAdvancer (BookTranslation.Core/Abstractions/IBatchAutoAdvancer.cs, implemented in BookTranslation.Infrastructure/Jobs/BatchAutoAdvancer.cs) driven from the JobProcessor, with the Worker as a backstop so an in-flight book reaches completion even if the API process churns.
The project state machine becomes:
Created ──Normalize──► Calibrating ──ProcessNextBatch (1 page, auto-confirm)──► Processing
Processing ──auto-advanced ProcessNextBatch (5 → 25 → 100 → rest, each auto-confirms)──► Processing …
Processing ──(no Pending pages left)──► CompleteOptions considered.
| Option | Verdict |
|---|---|
| (a) Keep the calibrate-narrow ladder, advance it automatically | ✅ Chosen. Keeps the proven "don't bulk-process before the first page looks sane" instinct and the resumable per-batch transitions, while removing the human pause. |
| (b) Drop the ladder, process the whole book in one batch | ❌ Loses the cheap early signal (a totally broken OCR config is caught after 1 page, not after 1000) and the granular resumability; no upside once the human gate is gone. |
Trade-offs. The "calibration page" no longer gives a human a look before scale — it is now purely a system warm-up / early-failure signal. We keep it because it is nearly free and preserves the batch-granular resumable transitions; removing it bought nothing.
4. D3 — Affordability is the only gate (+ retry)
Recommendation. The only thing that pauses an in-flight book is money. The pre-commit cost estimate (#1375) plus the wallet are the sole brake. When the next batch would exceed the balance, the auto-advance pauses cleanly at Processing — not an error — and the UI shows the "Add credit" CTA (#1396); topping up resumes the chain exactly where it stopped. The one remaining explicit recovery control is retry of a Failed page (Failed → Processing), owning the affordance #1340 referenced and #1367 stories.
Options considered.
| Option | Verdict |
|---|---|
| (a) Pause cleanly + "Add credit" + auto-resume on top-up | ✅ Chosen. Treats running out of credit as an expected state, not a failure; resumable by construction. |
| (b) Hard-fail the project when over balance | ❌ Punishes a normal, recoverable situation; loses work / confuses the user. |
| (c) Let it overdraw and bill later | ❌ Unacceptable for a prepaid wallet model; invites abuse and disputes. |
Trade-offs. A long book can stop midway if the wallet runs dry — but that is the desired behaviour for a prepaid service, and the clean pause + one-click top-up makes it a non-event. The upfront estimate (#1375) is what keeps this rare: the user commits knowing the projected cost.
5. D4 — Read-only reading experience + single completion notification
Recommendation. The review screen is now a read-only reading view. The user pages through the translation (each page's original scan beside its English), sees per-page status, and exports — there are no approve / reject / edit decision buttons. The completion notification fires once, when the whole book finishes ("Translation ready"), replacing the old per-batch "a batch is ready for your review" notification (#1352, reframed).
Trade-offs. Fewer touch-points means the user gets one clear "it's done" signal instead of a stream of per-batch prompts — consistent with the hands-off product. Resume-where-you-left-off (#1353) stays, but now it resumes reading, not reviewing.
6. Explicitly preserved (NOT removed by this pivot)
The pivot is surgical — it removes the human gate, not the engine. These all survive unchanged in intent:
- The resumable state machine. Every operation is still one discrete, guarded, persisted transition;
project.jsonis still the authoritative resumable state; a fresh process still continues from disk. Auto-advance chains transitions; it does not make them implicit. - The calibrate-first batch ladder. 1 → 5 → 25 → 100 → rest, capped to remaining
Pendingpages.BatchPolicyis untouched. - The spend / affordability gate. The estimate-vs-balance cost gate and the credit wallet are now the primary (and only) gate, not a secondary one.
- Failed-page retry. The one explicit recovery control (
Failed → Processing). - The per-page pipeline. normalize → layout → OCR → language detect → block-by-block translate → assemble → export, all as in
spec.md §4.
7. Out of scope / future
- Per-page-range selection — "upload a PDF and translate only pages X–Y / specific pages" (#1386, #1376). The automatic flow currently translates the whole uploaded book; selecting a sub-range to pay for is a separate, future enhancement, not part of this pivot.
- An additional cost-estimate requirement — any tightening of the upfront estimate (e.g. a hard confirm step, per-file actual-vs-estimate reconciliation beyond #1397–#1399) is tracked as a separate PBI, not folded in here.
- Translation-quality controls — the optional LLM polish pass and per-book OCR-engine choice (Kraken vs Surya for historical type) remain M3 work in
spec.md §8; they are the real quality lever now that per-page human correction is gone.
8. Impact on existing docs + tickets
spec.md— §1 goal, §2 principles, §3 state machine (project + page diagrams), §4 pipeline step 6, the batch-ladder note, §7 API table (approve/reject removed, retry surfaced), §8 M1 description, and §9 testing table were rewritten to the automatic model.user-stories.md— product-intent header, the Reader/Translator persona, Feature #1365 (renamed Core Translation & Reading Experience; #1366 edit-before-approve and #1368 confirm-source-language retired; #1367 retry survives), the retro-documented flows in §6 (approve/reject removed; read-only reading + auto-advance documented), #1340/#1352 queue + notification wording, and the onboarding gap.CLAUDE.md/README.md— the top-line one-liners that described a "human approval gate between every batch escalation" were corrected to the automatic-service model; the Worker description changed from "continues approved projects, never approves" to "auto-advances in-flight projects to completion."- Feature #1365 premise ("the human-in-the-loop quality gate is the product's reason to exist") is retired; this PBI subsumes the auto-advance work.