Gsub produciendo resultados diferentes ejecutando el mismo código

Las variables como $1 se asignan después de que se realiza una coincidencia de expresión regular. Simplificando un poco tu ejemplo:

s.gsub('original(value)', "replacement#{$1}")

La expresión "replacement#{$1}" se evalúa antes de que se llame a la función gsub. Por lo tanto, $1 contendrá el valor residual de alguna tarea de expresión regular anterior. (Por eso tu segundo intento funciona: toma $1 del primer intento).

Hay varias opciones para resolver este problema. gsub ofrece muchas funcionalidades diferentes.

Mi preferencia es pasar un bloque a gsub. El bloque se evalúa después de que se ha realizado la coincidencia de la expresión regular, por lo que $1 funcionará como esperas:

s.gsub('original(value)') { |match| "replacement#{$1}" }

O bien, puedes utilizar la característica de «referencia trasera» de gsub. No soy fanático de esta sintaxis, pero funciona. En lugar de usar "replacement#{$1}", usa 'replacement\\1' (o \\2, \\3, etc.).

s.gsub('original(value)', 'replacement\\1')