正規表現の一致が実行された後に、$1 のような変数が値が設定されます。例を少し簡略化してみましょう:
s.gsub('original(value)', "replacement#{$1}")
"replacement#{$1}" という式は、gsub 関数が呼び出される前に評価されます。そのため、$1 は過去の正規表現処理から残った値になってしまいます(これが、2 回目の試みが機能する理由です。最初の試みからの $1 を使用しているためです)。
この問題を解決する方法はいくつかあります。gsub には多様な機能があります。
私のおすすめは、gsub にブロックを渡す方法です。ブロックは正規表現の一致の後に評価されるため、$1 が期待通りに機能します:
s.gsub('original(value)') { |match| "replacement#{$1}" }
あるいは、gsub の「バックリファレンス」機能を使用することもできます。この構文は好みではありませんが、機能します。"replacement#{$1}" の代わりに 'replacement\1'(または \2、\3 など)を使用します。
s.gsub('original(value)', 'replacement\1')