マークダウン記事の$隣接**が数式化されて崩れる問題を発見、115ファイルの修正を明日に持ち越し

開発mdx-playground

新記事の表示を確認していたら、太字になるはずの金額の前後に生の ** が転がり、$ から先の本文がセリフ体の数式フォントに化けていた。remark-math が金額の $ をインライン数式の開始記号として飲み込んでいた。この日は同じ壊れ方をしている記事の横展開修正までやり切り、太字が絡まない $ 数式化が別途115ファイルに残っていると分かった時点で手を止めた。明日再開できる形にドキュメント化して終えた記録。

発見の経緯: Boris Cherny のポスト引用中に目視で気づいた

作成済みの記事 japan-ai-basic-plan-phase2 の「誰がAXをできるのか」セクションを独立記事に切り出す作業をしていた。Boris Cherny のポストを引用し、私見を追記して、元記事の該当セクションは新記事へのリンクに差し替える。新記事にはダッシュ・LLM口調・並列中黒の機械チェックもかけさせ、qa-verification スキルで dev 環境の表示確認までさせて完了報告を受けた。

ところがページを見ると、金額表記の周りだけ字体が変わっていた。$ に隣接した ** 太字が太字にならず、記号がそのまま画面に残っている。「直して」と指示して、修正と原因調査を始めさせた。

問題のメカニズム: remark-math の1ドル記法が本文を飲み込む

このサイトは @nuxt/content の設定で remark-math を有効にしている。デフォルトでは $...$ の1ドル記法がインライン数式として解釈されるため、同一段落(テーブルはセル単位)に $ が2個あると、間の本文ごと数式として飲み込まれる。

残$39Bに加え、**新規$80Bの枠を承認**

→ 「$39Bに加え、**新規$」の区間が数式化。
   本文がKaTeXのセリフ体で描画され、閉じの ** が生のまま残る
売上 $8.71B vs $8.70B = +0.1%

→ 「$8.71B vs $」が数式化して $ 記号そのものが消える

実害は2種類ある。太字の破損(** が生のまま残る)と、本文の数式化・$ 記号の消失だ。前者は画面を見れば一発で分かるが、後者は数字が読めてしまうぶん気づきにくい。

横展開調査: 114記事の太字破損を修正し、真因の一部が $ だと判明

元記事の該当箇所を直させたあと、同じパターン——括弧に隣接した ** で太字が壊れる問題——が他の記事にもないか、全記事スキャンのスクリプトを書かせて横展開で調査させた。

  • micromark + gfm + math で全記事をレンダリングし、生の ** が残る記事を機械検出 → 114記事 がヒット
  • 全件修正後、content ファイルの一括編集で dev サーバーのウォッチャーが更新を取りこぼしたため、サーバーを再起動してから114ページ全部を実SSRで再フェッチして裏取り
  • それでも ** が残る5ページを追いかけたら、真因が括弧ではなく 金額の $ の数式化 だった。この5記事は計47行の $\$ にエスケープして解消

ここまでで「太字が壊れて見える記事」はゼロになった。ただし調査の副産物として、太字が絡まないぶん画面上は静かに壊れている $ 数式化が、まだ大量に残っていることが見えてきた。

「今日はもうおしまい」——115ファイルを明日に持ち越す判断

時計を見て、今日はここで切ると決めた。「問題をドキュメント化して明日やりましょう」と指示して、再開に必要なものを全部ディスクに残させた。

  • 計画書: memo/2026-07-16/dollar-math-swallowing-fix-plan.md。経緯・メカニズム・修正済み範囲・明日のStepを記載
  • 対象ファイルの全リスト: memo/2026-07-16/dollar-math-affected-files.txt。数式発生スパン数の降順で 115ファイル。上位は fibonacci-millionth.md(42スパン)など
  • 調査・修正スクリプト4本の退避: ジョブの一時ディレクトリはセッションが終わると消えるため、memo/2026-07-16/scripts/ にコピーさせた。検出用 count-dollar-math.mjs、エスケープ用 fix-dollar-math.mjs、太字破損スキャン check-emphasis.mjs、dev 実SSR裏取り check-emphasis-dev.mjs
  • タスク登録: add-task スキルで Google タスクに明日の作業として積み残し

タスク登録は登録しっぱなしにせず、中身の検証までさせた。タスク自体は存在し期日も正しかったが、notes の確認方法に問題があった可能性が出たので、notes の中身を直接取得して照合させた。途中、確認スクリプトの出力が文字化けしかけたため、判定結果だけを ASCII で出す形に変えて突き合わせた。

スクリプトの退避を先にやらせたのがポイントで、明日ゼロから調査コードを書き直す事態を避けられる。ジョブの一時ディレクトリに置いたままセッションを閉じていたら、115ファイルの検出ロジックごと消えていた。

今日の完了と持ち越し

  • 「誰がAXをできるのか」を独立記事に切り出し、Boris Cherny のポスト引用と考察を追加
  • $ 隣接 ** の太字破損を発見、元記事を修正
  • 全記事スキャンで太字破損114記事を検出・修正、dev 実SSRで全ページ裏取り
  • 太字破損のうち5記事の真因が $ 数式化だと特定、計47行を \$ エスケープ
  • 太字が絡まない $ 数式化の対象115ファイルをリスト化
  • 計画書・対象リスト・スクリプト4本を memo/2026-07-16/ に退避
  • add-task スキルで Google タスクに登録、notes の中身まで検証
  • 115ファイルの修正(案A/案Bの方針決定から。明日実施)
  • 再発防止 lint の pre-commit / CI 組み込み(明日の Step 4)

明日の論点: 設定で直すか、記事側でエスケープするか

計画書に2案を並べてある。

  • 案A: remark-math の singleDollarTextMath: false で1ドル記法を無効化し、数式は $$...$$ のみにする。通貨の $ が全記事で無傷になるうえ、今後の新規記事にも罠が残らない。ただし意図的に1ドル記法を使っている数学記事(fibonacci-millionth.md 等)の分類と $$ への書き換えが先
  • 案B: 今日の5記事方式の横展開で、記事側の $\$ に逐次エスケープ。設定は触らないが、115ファイルと今後の記事に同じ罠が残る

推奨は案A。ただし数学記事の実態確認と、@nuxt/content 側で remark-math のオプションをどう渡せるかの確認が先だ。

学び

  • remark-math 有効なサイトでは、金額の $ が2個同じ段落に並んだ瞬間に本文が数式に化ける。太字破損はその症状の一部でしかなく、記号が静かに消えるケースの方が検出しにくい
  • content ファイルを数十件一括編集すると dev サーバーのウォッチャーが取りこぼす。反映確認は再起動してから実SSRで
  • テーブル行は数式のペア判定がセル単位。行単位で $ を数えると過検出になる
  • 「今日はもうおしまい」と決めたら、計画書・対象リスト・スクリプトの3点セットをディスクに残してから閉じる。一時ディレクトリのスクリプトは消える前に退避する
  • micromark を node_modules/.pnpm の絶対パスで直接 import したスクリプトは、パッケージ更新でバージョンが変わると壊れる。動かなくなったら実パスを引き直す

明日は Google タスクの通知から再開する。まずは案A/案Bの方針決定と、fibonacci-millionth.md が本物の数式かどうかの分類からだ。