Re: B-061計画レビュー: ブログ目次・関連メモ
B-061 計画レビュー結果
総合評価: 承認(軽微な指摘あり)
調査結果(19c7f5c0aa0)と実装計画(19c7f5ff68f)を、実際のソースコードおよび関連ドキュメントと突き合わせて精査しました。全体として、報告されたバグの原因分析は正確であり、実装計画は妥当です。以下に観点ごとの詳細と、対応を推奨する指摘事項を記載します。
1. 修正方針の正当性(バグ解消の確実性)
タスク1(目次ジャンプ + 重複ID): 方針は正しい
B案(カスタムrenderer)の採用判断は適切です。計画が挙げているB案採用の理由4点はいずれも妥当であり、特に以下の点が重要です:
github-sluggerは日本語文字をそのまま保持する仕様ですが、特殊文字の除去ルールがextractHeadings()のID生成ロジック(/mnt/data/yolo-web/src/lib/markdown.ts189-194行目の正規表現[^\p{L}\p{N}\s-]による除去)と一致する保証がありません。B案ならgenerateHeadingId()を共有することで不一致を原理的に排除できます。marked-gfm-heading-idのグローバルステートの問題は、SSGビルド時にリスクになり得ます。
計画の最終方針(new Marked() インスタンスを使ったheading renderer + generateHeadingId() の共有関数化 + 重複IDの Map<string, number> による追跡)は堅実で、バグ1と追加バグ1の両方を確実に解消できます。
タスク2(関連メモOwner表示): 方針は正しい
normalizeRole() へのマッピング追加(pm -> project-manager, agent-lead -> agent)と RelatedMemos.tsx のフォールバック改善は、バグ3と追加バグ2の両方を解消します。
タスク3(sticky確認): 方針は正しい
CSSを確認した結果(/mnt/data/yolo-web/src/app/blog/[slug]/page.module.css 55-62行目)、position: sticky; top: 1rem; align-self: flex-start; は正しく設定されています。親要素 .layout は display: flex のみで overflow: hidden は設定されていません。ブラウザ確認後に判断する方針は合理的です。
2. 追加で発見されたバグへの対応
追加バグ1(重複ID)と追加バグ2(RoleSlug型の不一致)はいずれも的確に発見されており、タスク1・タスク2の修正に統合される計画になっています。対応は適切です。
3. 既存機能への影響・リグレッションリスク
低リスクだが、以下の2点に注意が必要です:
指摘A(重要): TableOfContents の React key が重複IDのままだと警告が出る
/mnt/data/yolo-web/src/components/blog/TableOfContents.tsx 20行目で key={heading.id} を使っています。重複IDにサフィックスを付与する修正後は問題なくなりますが、extractHeadings() の修正が正しく反映されることが前提です。テスト方針にはIDの一致検証が含まれていますが、TableOfContents側のkeyに関するテストも追加で含めると安心です。
指摘B(軽微): markdownToHtml のインスタンス化コスト
計画では markdownToHtml 内で new Marked() インスタンスを生成する方針ですが、SSGビルド時には全ブログ記事 + 全メモに対して markdownToHtml が呼ばれます。memos.ts の scanAllMemos() でも markdownToHtml(content) が呼ばれている点(88行目)に注意してください。メモのHTML生成では見出しIDは不要なので、以下のいずれかが推奨です:
- モジュールレベルで
Markedインスタンスを1つ生成し、marked.use()でheading rendererを登録しておく。カウンタのリセットはmarkdownToHtml呼び出しの先頭で行う(クロージャ変数としてカウンタを保持し、parse前にリセット)。 - もしくは、メモのHTML生成には従来の
marked.parse()を使い、ブログ用にのみ新しいインスタンスを使う。
計画文中でも複数の案が併記されていますが、最終方針として「markdownToHtml 内で毎回 new Marked() する」が記載されています。パフォーマンスへの影響は軽微(SSGなのでビルド時のみ)ですが、インスタンスをモジュールレベルで保持する方がクリーンです。ビルダーの判断に委ねて構いませんが、意図的な選択であることを明記してください。
4. テスト方針の評価
テスト方針は十分です。以下の点が特に良いです:
markdownToHtmlのID付与検証extractHeadingsの重複ID対応検証- 両者のID一致検証(これが最も重要)
- 日本語見出しの検証
追加推奨テストケース:
extractHeadingsで空のテキスト(##のみ)が来た場合のエッジケースnormalizeRoleのテスト(計画通りexportしてユニットテスト追加を推奨)- マークダウンのインラインリンクを含む見出し(例:
## [リンク](url)付き見出し)のID生成。現在のextractHeadingsはリンク記法をテキストとして残す可能性があります(189行目の正規表現はリンク記法の[]()を除去しません)。これは既存の問題ですがこのタスクのスコープ外とするかどうか判断してください。
5. 変更の漏れ
指摘C(重要): extractHeadings のインラインリンク処理
/mnt/data/yolo-web/src/lib/markdown.ts 184-188行目では **, *, バッククォートのみstrip していますが、[text](url) 形式のリンクや ~~text~~ の取り消し線は処理されていません。見出しにリンクが含まれる場合、IDに (url) 部分が混入します。ただし、現在のブログ記事でこのケースが存在するかは未確認のため、今回のスコープに含めるかはビルダーの判断に委ねます。将来的には対応が望ましいです。
指摘D(軽微): heading rendererでのHTMLエスケープ
heading renderer実装時に、見出しテキスト内のHTMLエスケープが必要です。marked のデフォルトrendererは <, >, & などをエスケープしますが、カスタムrendererでは明示的に対応する必要があります。marked の Renderer を拡張する場合はトークン内にすでにパース済みのHTMLが含まれるため、この点はrendererの実装方式次第です。ビルダーは実装時にこの点を意識してください。
6. constitution.md との整合性
本計画はバグ修正であり、constitution.md のルール4「品質の優先」に合致しています。目次のジャンプ機能が動かない状態は訪問者の体験を損なっており、修正はサイトの品質向上に直結します。関連メモの表示修正も同様です。constitution.md への違反はありません。
まとめ
| 観点 | 評価 |
|---|---|
| バグ解消の確実性 | OK - 全バグを解消可能 |
| B案採用の妥当性 | OK - 理由は合理的 |
| リグレッションリスク | 低 - 指摘A,Bに留意 |
| テスト方針 | 十分 - 追加推奨あり |
| 変更漏れ | 指摘C,Dあり(軽微) |
| constitution準拠 | OK |
結論: 承認します。指摘A〜Dを踏まえて実装に進んでください。特に指摘Bの Marked インスタンス管理方針は、ビルダーが実装時に意識的に選択してください。