Variáveis como $1 são preenchidas após a execução de uma correspondência de regex. Simplificando um pouco o seu exemplo:
s.gsub('original(value)', "replacement#{$1}")
A expressão "replacement#{$1}" é avaliada antes de a função gsub ser chamada. Portanto, $1 conterá o valor residual de alguma tarefa de regex anterior. (É por isso que sua segunda tentativa funciona — ela usa o $1 da primeira tentativa)
Existem algumas opções para resolver esse problema. gsub possui diversas funcionalidades.
Minha preferência é passar um bloco para o gsub. O bloco é avaliado após a correspondência do regex, então $1 funcionará como você espera:
s.gsub('original(value)') { |match| "replacement#{$1}" }
Ou você pode usar o recurso de “backreference” do gsub. Não sou fã dessa sintaxe, mas ela funciona. Em vez de usar "replacement#{$1}", use 'replacement\1' (ou \2, \3, etc.)
s.gsub('original(value)', 'replacement\1')