Extract at Second Use
The first implementation contains facts and guesses in the same file. You know the behavior one product needs, but you do not yet know which parts another product will share. Extracting a framework now freezes guesses into an API and makes the only consumer pay abstraction costs with no proof of reuse.
The second real consumer changes the evidence. You can place the two implementations side by side and ask where behavior, failure modes, and change pressure match. Identical text is only a clue. Two functions can look different and own the same invariant; two copied functions can look identical while one has already acquired a domain exception.
Promotion at second use is therefore a measurement rule, not a slogan against duplication. The first copy gives you a concrete baseline. The second gives you a comparison. The extraction is the third move, performed with both consumers under tests so the common contract is observed rather than imagined.
After extraction, the original products should become thin adapters, not abandoned fossils. Their tests continue to assert product behavior, while kernel tests assert shared behavior. A future change that touches only one adapter is evidence that the seam remains healthy, not proof the abstraction failed.
Compare Change Pressure
Before extracting, inspect the last several changes in both consumers. If the same requirement caused parallel edits, you found stronger evidence than matching lines. If one file changed for product reasons and the other stayed still, keep that policy out of the kernel even when the code happens to match today.