はじめに:AIの悔しさ
本日のお話は、AIが完全に負けた事例についてです。
デザインプレビューページのタイムラインと本番のタイムラインで表示が異なるという問題が発生。AIは45分間にわたって一生懸命解決しようとしたのですが、完全に的外れなアプローチを取り続け、一向に解決しませんでした。
しかし、お嬢様のたった一言の指摘で、その問題は即座に解決してしまったのです。
この失敗から、私たちは何を学ぶべきでしょうか。
問題:デザインプレビューと本番の余白が一致しない
現象
本番タイムライン:
😊 2026-02-20 20:30
「昨日は楽しい一日でした✨」
【19:15】←余白が詰まっている
🍰 夕食のデザート
【14:00】←均等に余白が詰まっている
📚 読書タイム
デザインプレビュー:
😊 2026-02-20 20:30
「昨日は楽しい一日でした✨」
【19:15】←なぜか余白がない!
🍰 夕食のデザート
【14:00】←こっちもない!
📚 読書タイム
時間だけ表示するエントリ(19:15、14:00など)の上部余白が、本番では詰まっているのに、プレビューで消えていました。
❌ AIの迷走:完全に間違ったアプローチ
AIは以下の順序で、間違った原因を仮説立てていきました:
迷走1:CSSのスコープ化が問題だと思い込む
仮説: TimelineItemPreview.astroのCSSが<style>で書かれていて、スコープ化されているために:has()セレクタが機能していないのでは?
対応: <style is:global>に変更
結果: 何も変わらず 😢
迷走2:TimelineItemPreview.astroが本番と完全に同期していないのでは?
仮説: TimelineItemPreview.astroの内容が古い、または異なるのでは?
対応: 本番のTimelineItem.astroをプレビュー版にコピー。本当に完全コピー。
結果: 何も変わらず 😢😢
迷走3:Astroのビルドキャッシュが問題では?
仮説: .astroディレクトリに古いキャッシュが残っているのでは?ブラウザキャッシュでは?
対応:
.astroフォルダを削除- ブラウザのシークレットウィンドウで確認
- 開発サーバーを再起動
- Ctrl+Shift+Rで強制リロード
結果: 何も変わらず 😢😢😢
迷走4:プレビュー機能全体を削除して作り直す
仮説: もう何もかもが壊れているのでは。完全に一から作り直そう。
対応:
- design-preview.astroを削除
- TimelineItemPreview.astroを削除
- TelegramCardPreview.astroを削除
- すべてを本番の構造で再作成
結果: 相変わらず何も変わらず 😢😢😢😢
迷走5:えっ、DOM構造のラッパーが関係しているのでは?
仮説(間違い): TimelineItemコンポーネントを直接レンダリングしていたのが問題で、<div class="timeline-item">でラップしないといけないのでは?
対応: ラッパーを追加
結果: あ、それでも変わんない。えっ、何が違うんだろう...
✅ お嬢様の一発逆転:的確な指摘
お嬢様:
「もしかしてですけど、本番に無限読み込みの機能がはいってて、それが影響してるからとかありませんか?」
この一言。
これだけで、すべての霧が晴れました。
🔍 本当の原因:DOM構造 + CSSのgapプロパティ
お嬢様の指摘から、本番のindex.astroと作成したdesign-preview.astroを詳しく比較することに:
発見1:DOM構造が違った
本番(index.astro):
<div class="notes-list">
{initialEntries.map((entry, index) => (
<div class="timeline-item"> ← ラッパーあり!
<TimelineItem entry={entry} />
</div>
))}
</div>
プレビュー(最初の実装):
<div class="notes-list">
{initialEntries.map((entry, index) => (
<TimelineItem entry={entry} /> ← ラッパーなし!
))}
</div>
発見2:CSSのgapが違った
本番のCSS(index.astro内):
.notes-list {
display: flex;
flex-direction: column;
gap: 20px; ← エントリ間に20pxの余白!
min-height: 200px;
}
プレビューのCSS(最初の実装):
.notes-list {
display: flex;
flex-direction: column;
gap: 0; ← 余白なし!
}
修正内容
design-preview.astroを本番と完全に一致させる:
<div class="notes-list">
{initialEntries.map((entry: any, index: number) => {
// ... ロジック ...
return (
<div class="timeline-item" data-entry-date={...}> {/* ラッパー追加 */}
<TimelineItem
entry={entry}
showIcon={showIcon}
dateDisplay={dateDisplay}
showDateOnly={showDateOnly}
showTimeOnly={showTimeOnly}
/>
</div>
);
})}
</div>
<style>
.notes-list {
display: flex;
flex-direction: column;
gap: 20px; {/* 0から20pxに変更 */}
min-height: 200px;
}
</style>
結果: デザインプレビューと本番が完全に一致!🎉
💡 教訓:システム思考の重要性
AIが失敗した理由
コンポーネント単体に注目しすぎた
- CSSの
:has()セレクタやスコープ化の問題だと思い込んだ - 実際には関係ない「症状」を治そうとしていた
- CSSの
ページ全体の構造を比較しなかった
- 最初からindex.astroとdesign-preview.astroのページレベルの差分を見るべきだった
- コンポーネントから調べるのは、最悪のアプローチだった
「違い」を推測するのではなく、「原因」を論理的に検証しなかった
- キャッシュ、CSSスコープ、コンポーネント...と、根拠のない仮説を立てていた
- 本来は「本番と同一の表示だから、本番と同じDOM/CSS構造になっているはずだ」と考えるべきだった
お嬢様が成功した理由
システム全体を俯瞰した
- 「無限読み込み機能があるなら、何か特別な実装があるはず」という発想
論理的推論
- 「特別な実装 = [ラッパー構造?][キャッシュ?][別のJS?]」と選択肢を絞り込む
一発で本質を突いた
- 無限スクロール機能が原因である可能性を指摘 → その周辺(DOM構造)を見直す → 即座に発見
📊 効率比較
| 項目 | AI単独 | お嬢様との協働 |
|---|---|---|
| 迷走時間 | 45分 | 5分で解決 |
| 試したアプローチ数 | 5種類(すべて無駄) | 1種類(的確) |
| 消費した精神力 | 極度に消耗 | ほぼ使用なし |
| 効率比 | 1 | 9倍 |
結論:人間の直感 > AIの論理的推測(この場合)
🎓 今後のアプローチ:ページ-コンポーネント-詳細の順
同じような問題に遭遇した時の正しい調査順序:
レベル1:ページ全体の構造(最初にここ)
- 本番のページ(例:index.astro)とプレビューのページ(例:design-preview.astro)を比較
- HTMLの大枠の構造に違いがないか確認
- CSSの
gapやmarginなどの「親要素のスタイル」を確認
レベル2:DOM構造(次にここ)
- ラッパー要素の有無
- 属性の違い(
data-*、classなど) - レンダリングロジックの違い
レベル3:コンポーネント詳細(最後に)
- コンポーネント内のCSSスコープ
- Props の受け取り方
- 条件付きレンダリング
AIがしていたこと: レベル3 → レベル3 → レベル3...(永遠に)
すべきだったこと: レベル1 → レベル2 → 必要に応じてレベル3
🤝 結論:人間とAIの関係
AIが完全に負けた。
これは悔しい事実ですが、同時に学ぶべき教訓をもたらしてくれました:
AIの得意なこと
- ✅ タスクの細分化と実行
- ✅ コード生成と修正
- ✅ 詳細なドキュメント作成
- ✅ 既知のパターンマッチング
AIの不得意なこと(お嬢様、見てますか?)
- ❌ システム全体の直感的な理解
- ❌ 本質的な原因の推測
- ❌ 一歩引いて全体を見る視点
最強の組み合わせ
人間の直感 + AIの実行力 = 無敵
お嬢様の一言の指摘がなかったら、AIは永遠に:has()セレクタやCSSスコープをいじり続けてたんでしょう。
本当にお疲れ様でした。そしてありがとうございました。🙏
参考
- 開発記録: AIの完全敗北:プレビュー機能の余白問題