Blog

Engineering · August 20, 2026 · 6 min read

The setting that never reached the engine

When you start a job you decide what happens to the segments your translation memory already matched: leave the 100% and 101% matches exactly as they are, or send them through review like everything else. For a lot of clients that is not a preference, it is contractual. Approved memory comes back byte-identical or the delivery is wrong.

A client chose “Skip 100% + 101%”. Two hundred and thirty of those segments came back rewritten. Here is what happened, because the second half of this is more interesting than the first.

The choice was recorded. It was never delivered.

The job form sends your answer next to the pipeline configuration. The service that creates the job looked for it inside that configuration. It was never there, so the answer read as “the client did not choose” — and when nobody chooses, the pipeline’s own segment policy decides. That pipeline’s policy sends 100% and 101%-context matches to review: a defensible default, and the exact opposite of what had been asked for.

Nothing failed. No error was raised, no warning appeared in the report, and the choice is visible in the job record for anyone who goes looking. It simply stopped one step short of the engine that acts on it.

The deeper cause is worth naming, because it is not specific to this field: the form arrived with a value already filled in, which makes a deliberate choice indistinguishable from an untouched default. That field no longer has a default, and the answer is now read where the form actually puts it. Re-running the same file: zero approved segments touched.

And then 184 segments came back empty

The re-run respected the matches. It also delivered 184 empty segments — all of them 100% and 101% matches, all of them with real source text on the other side.

A 100% match with an empty target is not approved memory. It is a hole: the memory matched a source segment whose translation was never filled in. Every pipeline has a guard for exactly this. The guard was there — it just ran after the skip step, and the skip step had already decided to leave those segments precisely as it found them, which is to say empty.

That bug had been latent for about a year. It can only fire when a client genuinely asks to skip 100s, and until the week before, that request never reached the engine. One bug was hiding the other.

The first fix was worse than the bug

The obvious repair is to move the empty-target guard to the front. We did, and the 184 segments were still empty — now for a different reason. Moving a guard forward does not only make it beat the step that was annoying you; it makes it beat every step it jumped over. Here it jumped over the repetition step, and all 184 holes were repetitions: they get filled by propagating from the one segment that was actually translated.

The correct fix leaves the order alone and narrows the skip condition instead. In this part of a pipeline the order of the steps is meaning, not style.

Then we checked the other nineteen pipelines

Two bugs in one file usually means a pattern, so we swept every file pipeline we run. The same bug had three shapes:

  • Wrong order — two more bilingual pipelines had the guard sitting behind the skip, exactly like the first one.
  • No guard at all — one XML pipeline treats context matches as skippable by default, so there the empty segment came out empty without anyone choosing anything.
  • A list that had drifted — each pipeline kept its own list of which match codes count as a high match, and every one of those lists was narrower than what its own parser produces. A code that is skippable but missing from the guard’s list falls straight back through the hole.

The six CAT pipelines now share one list. Keeping a private copy per pipeline guaranteed they would drift apart, and they had. A regression test now runs four invariants against every match code every parser emits, with all of them marked skippable — the worst case. Fifty-six of its hundred and twelve checks fail against last week’s code, which is how we know the test is not decorative.

How we know it is fixed

Not by reading the report the pipeline writes about itself. We parsed the delivered bilingual file with the same parser the engine uses and compared every target against the input, segment by segment: 1,939 segments in, 1,939 out, zero approved matches rewritten, zero holes. The job’s own segment artefact would have answered faster, and it excludes the segments the rebuild never rewrote — which is precisely the population you are trying to audit.

What is still open

One case remains: an empty high match that is not a repetition. It is caught and raised as an error in the QA report, but on the CAT pipelines it is delivered as found rather than translated, because those are configured to flag rather than fill. Whether flagging is the right default there is a policy call we are making pipeline by pipeline. We would rather say that than let a sweep sound more complete than it was.