Claude Code + Superpowers プラグインで Django アプリを自律実装→手動テスト→バグ修正した記録
本記事はClaude Codeで9割9分書いたものになります
TL;DR
- Claude Code の superpowers プラグインで 14タスク・75テスト・15コミットをユーザー介入ゼロで自律実装した
- その後の手動テストで 3件のバグと1件の機能不足 が見つかり、対話ベースで修正(+18テスト追加、計93テスト)
- 見つかったバグの内訳:htmxのswapパターンミス(E2Eテストでしか防げない)、UI表示と検索ロジックの不整合(仕様の穴)、createsuperuserとテンプレートの組み合わせ問題
- 教訓:AI自律実装の品質は入力ドキュメント(実装計画・規約・ユースケース)の精度に比例する。ただしライブラリ固有の落とし穴はドキュメントでは防げず、E2Eテストが必要
できたもの
概要
Django + htmx + Tailwind CSS で構築した社内コミュニティ活動募集プラットフォームにおいて、Claude Code の superpowers プラグインを使い、14タスクの自律実装からバグ修正・新機能追加までを行った記録です。
特徴的なのは2つのフェーズに分かれている点です:
-
自律実装フェーズ(セッション①) —
/superpowers:subagent-driven-developmentで Task 1〜13 を ユーザーの介入ゼロ で自動実装。サブエージェント(Sonnetモデル)がタスクごとにTDD→コミットを繰り返し、最後にコードレビュー→修正まで完了 - 手動テスト&修正フェーズ(セッション②) — Task 14(全体テスト・手動確認)を起点に、ユーザーとの対話ベースでバグ修正・新機能追加
環境
- プロジェクト: Django 5.x / htmx / Tailwind CSS
- ツール: Claude Code (Claude Opus 4.6)
- プラグイン: superpowers@claude-plugins-official
セッション①:自律実装(Task 1〜13)
起動
ユーザーが /superpowers:subagent-driven-development を実行。以降、ユーザーのメッセージは0件。Claude が自律的に以下を実行しました。
実行の流れ
- 実装計画・設計書・コーディング規約を読み込み
- フィーチャーブランチを作成
- タスクごとにサブエージェント(Sonnetモデル)をディスパッチし、TDDで実装→コミット
- 依存関係を考慮してタスク順を変更(Task 10a を Task 10 の前に実行)
- 全タスク完了後にコードレビューサブエージェントを起動→指摘修正
Claude のログ(抜粋)
サブエージェント駆動開発を開始します。まず実装計画と必要なドキュメントを読み込みます。
プロジェクトはまだドキュメントのみで、コードは未実装です。14タスクを順次実行します。
[サブエージェント起動] Implement Task 1: プロジェクトセットアップ (model: sonnet)
Task 1完了。スペックレビューをスキップしてTask 2に進みます(セットアップのみで仕様逸脱リスクが低いため)。
[サブエージェント起動] Implement Task 2: Tailwind + htmx (model: sonnet)
Task 2完了。Task 3に進みます。
[サブエージェント起動] Implement Task 3: CustomUser model (model: sonnet)
Task 3完了(4テスト全パス)。
[サブエージェント起動] Implement Task 4: allauth auth views (model: sonnet)
Task 4完了(7テスト全パス)。allauth v65のAPI変更に適切に対応。
[サブエージェント起動] Implement Task 5: tags app (model: sonnet)
[サブエージェント起動] Implement Task 6: communities models (model: sonnet)
Task 6完了(7テスト全パス)。
[サブエージェント起動] Implement Task 7: communities views (model: sonnet)
Task 7完了(17テスト全パス)。
[サブエージェント起動] Implement Task 8: events models (model: sonnet)
Task 8完了。Task 10a → Task 10 の順で実装します(badgesのシグナルがnotificationsに依存するため)。
[サブエージェント起動] Implement Task 10a: notifications app (model: sonnet)
[サブエージェント起動] Implement Task 10: badges engine+signals (model: sonnet)
Task 10完了(13テスト全パス)。
[サブエージェント起動] Implement Task 9: events views+templates (model: sonnet)
Task 9完了(35テスト全パス)。
[サブエージェント起動] Implement Task 11: search feature (model: sonnet)
[サブエージェント起動] Implement Task 12: home page (model: sonnet)
[サブエージェント起動] Implement Task 13: mypage (model: sonnet)
Task 13完了(11テスト全パス)。
75テスト全パス!
[サブエージェント起動] Final code review of implementation (type: superpowers:code-reviewer)
コードレビュー完了。Critical/Importantの問題を修正します。
[サブエージェント起動] Fix review issues: critical+important (model: sonnet)
コードレビュー指摘の修正完了(75テスト全パス)。
コードレビューで修正された項目
- N+1クエリ問題の修正(event_detail)
- DBレベルフィルタへの改善(mypage)
- community_events_moreの本実装
- URL reverseの適用(通知サービス)
- 型ヒントの修正
結果
| 項目 | 値 |
|---|---|
| 実装タスク数 | 13 |
| コミット数 | 15 |
| テスト数 | 75(全パス) |
| ユーザーの介入 | 0回 |
セッション②:手動テスト&バグ修正(Task 14〜)
1. 残タスクの確認
ユーザー
/superpowers:subagent-driven-development 今残ってるタスクは何か
Claude
実装計画を読み込み、git履歴と照合。
結果: Task 1〜13は全てコミット済み。残りは Task 14(全体テスト・最終確認) のみ。
2. Task 14 実行(自動チェック)
Step 1〜3を並行実行:
Step 1: poetry run pytest -v → 75 passed ✅
Step 2: python manage.py migrate --check → No migrations to make ✅
Step 3: npx @tailwindcss/cli ... → Done in 47ms ✅
Step 4(手動確認)はユーザー自身で行う作業。ここから対話ベースに移行。
3. バグ発見①:「もっと見る」ボタンの無限ループ
バグについてキャプチャ撮ってないです。。
イベントが沢山あるとき、「もっと見る」ボタンが出るんですが、無限に「もっと見る」が押せてしまうバグを発見。
ユーザー
コミュニティ詳細で、イベントが4件しか存在しないとき、「もっと見る」ボタンを押下すると延々と4件目が繰り返し表示される。最大件数になったら、もっと見るボタンを非活性にするべき
Claude の調査
/superpowers:systematic-debugging スキルを起動し、根本原因を特定。
根本原因: 2つの問題があった。
-
detail.htmlのボタンが#events-listの外側にある —hx-swap="beforeend"でリスト内にコンテンツが追加されても、外側のボタン(offset=3ハードコード)は残り続ける -
_events_more.htmlのボタンもhx-swap="beforeend"— 新ボタンが追加されるが古いボタンも残る
修正
ボタンを #events-list 内に移動し、hx-swap="outerHTML" でボタン自身を置き換えるパターンに変更。
<!-- Before: ボタンが #events-list の外 -->
<div id="events-list">
{% for event in upcoming_events %}...{% endfor %}
</div>
{% if has_more_events %}
<button hx-get="...?offset=3" hx-target="#events-list" hx-swap="beforeend">
もっと見る
</button>
{% endif %}
<!-- After: ボタンがリスト内、outerHTMLで自身を置換 -->
<div id="events-list">
{% for event in upcoming_events %}...{% endfor %}
{% if has_more_events %}
<button id="load-more-events" hx-get="...?offset=3" hx-swap="outerHTML">
もっと見る
</button>
{% endif %}
</div>
テスト5件追加:
class TestCommunityEventsMore:
def test_load_more_returns_next_events(self, ...): ...
def test_load_more_hides_button_when_no_more(self, ...): ...
def test_load_more_shows_button_when_more_exist(self, ...): ...
def test_detail_page_no_button_when_three_or_fewer(self, ...): ...
def test_detail_page_button_when_more_than_three(self, ...): ...
4. バグ発見②:タグ検索の UX 問題
ユーザー
検索時、タグ検索で「#初心者歓迎」とハッシュタグ付で検索すると出てこないので、分かりやすくしたい。今の表示だと#付きで検索するように読める
やり取り
Claude: ビュー側で # を除去し、placeholderも変更します。
複数タグの検索はカンマ区切りですか?
ユーザー: カンマ区切りのOR検索で
Claude: 了解。実装します。
修正
ビュー(search/views.py):
# Before
tag = request.GET.get("tag", "").strip()
if tag:
community_ids = CommunityTag.objects.filter(
tag__name=tag
).values_list("community_id", flat=True)
# After
tag_input = request.GET.get("tag", "").strip()
tag_names = [t.strip().lstrip("#") for t in tag_input.split(",") if t.strip()]
if tag_names:
community_ids = CommunityTag.objects.filter(
tag__name__in=tag_names
).values_list("community_id", flat=True)
テンプレート:
- placeholder を
#タグ→タグ名(カンマ区切りで複数可)に変更 - 全画面のタグ表示をクリッカブルなリンクに変更(クリックで検索ページへ遷移)
テスト4件追加:
-
#付きタグ検索(コミュニティ / イベント) - カンマ区切り複数タグOR検索(コミュニティ / イベント)
5. 新機能:コミュニティ編集申請
要求漏れでした。完全に。
編集画面が無いので、作ったら作りっぱなし。。
ユーザー
コミュニティの編集画面がない
やり取り — 対話で仕様が段階的に明確化
Claude: 作成フォームと同じフィールドを編集できるようにする想定でしょうか?
編集権限はコミュニティのadminユーザーのみで良いですか?
ユーザー: 編集権限はコミュニティの管理者でOK。
編集内容は作成と同じ。
ただし、サイト管理者の承認を得る必要がある。
作成後、NGワードで更新されるのを防ぎたい
Claude は最初、編集時にコミュニティの status を pending に戻す実装を行った。
ユーザー: 承認待ちのステータスになると参加が出来なくなるのはよろしくない
この指摘を受けて方針を転換。
Claude: 編集内容を即座に反映せず、CommunityEditRequestモデルに保留する仕組みにしましょう。
- コミュニティのstatusはapprovedのまま維持
- サイト管理者がDjango Adminで承認すると反映
- 却下なら破棄
ユーザー: その方針でいきましょう
実装
新モデル(CommunityEditRequest):
class CommunityEditRequest(models.Model):
"""コミュニティ編集申請(サイト管理者の承認が必要)"""
community = models.ForeignKey(Community, on_delete=models.CASCADE, related_name="edit_requests")
requested_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
description = models.TextField()
image = models.ImageField(upload_to="communities/", blank=True)
tag_input = models.CharField(max_length=500, blank=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending")
def approve(self) -> None:
"""編集申請を承認し、コミュニティに反映する"""
self.community.name = self.name
self.community.description = self.description
if self.image:
self.community.image = self.image
self.community.save()
CommunityTag.objects.filter(community=self.community).delete()
save_tags(self.tag_input, self.community, CommunityTag, "community")
self.status = "approved"
self.save(update_fields=["status"])
ポイント:
- コミュニティの
statusはapprovedのまま → 参加・閲覧に影響なし - 編集申請中は詳細ページに「編集申請中」バッジ表示、編集ボタン非表示
- 既に pending の申請がある場合は新規申請不可
- Django Admin で承認・却下アクション対応
テスト9件追加(ビュー7件 + モデル2件)
6. バグ発見③:display_name が空のユーザーが見えない
ユーザー
(コミュニティ詳細で)参加者2名とあるが1名しか出ていない → 確認して
Claude の調査
# DBを直接確認
コミュニティ: ああああ
member_count: 2
- hoge@... / display_name="hoge" / role=admin
- sample@s.co.jp / display_name="" / role=member ← 空!
原因: テンプレートで {{ m.user.display_name }} を直接参照 → 空文字で何も表示されない
修正: {{ m.user.display_name }} → {{ m.user }} に変更(2箇所)
CustomUser.__str__ が display_name or email を返すため、空ならメールアドレスがフォールバック表示される。
コミット&マージ
コミット(機能ごとに分割)
0193cd9 fix: コミュニティ詳細の「もっと見る」ボタンが全件読み込み後も繰り返し表示されるバグを修正
896d3d0 feat: 検索機能を改善(#付きタグ対応・カンマ区切りOR検索・タグリンク化)
af8e8e7 feat: コミュニティ編集申請機能を追加(CommunityEditRequest)
58de5cb chore: Claude Code設定にsuperpowersプラグインを追加
マージ
git checkout master
git pull origin master
git merge feature/initial-implementation
git push origin master
まとめ
全体の成果
| フェーズ | 内容 | テスト数 | ユーザー介入 |
|---|---|---|---|
| セッション① | Task 1〜13 自律実装 | 75 | 0回 |
| セッション② | Task 14 + バグ修正 + 新機能 | +18(計93) | 対話ベース |
使ったスキル
| スキル | 用途 |
|---|---|
/superpowers:subagent-driven-development |
自律実装の起動・残タスク確認 |
/superpowers:systematic-debugging |
「もっと見る」ボタンのバグ調査 |
/superpowers:requesting-code-review |
全タスク完了後の最終レビュー |
どうしたらバグを防げたか
今回見つかった3つのバグについて、それぞれ振り返る。
バグ①「もっと見る」ボタンの無限ループ
原因: htmxの hx-swap="beforeend" とボタン配置の組み合わせ問題。サーバー側ロジックは正しく、Djangoのテストだけでは検出できない。
防ぐのが難しい理由: htmxの hx-swap モードの挙動(beforeend vs outerHTML)とDOM上のボタン配置の関係を理解していないと、そもそも何が問題かわからない。実装計画やコーディング規約に「outerHTMLパターンを使うこと」と書くにも、htmxに精通している必要がある。AIも人間も、知らないパターンはレビューで見つけられない。
現実的な対策:
- E2Eテスト(Playwright等)の導入が最も有効。 htmxの知識がなくても、「ボタンを2回押したら同じ項目が重複しないこと」「全件読み込み後にボタンが消えること」のような振る舞いベースのテストは書ける。DjangoのテストではHTMLレスポンスの中身は検証できても、ブラウザ上のDOM操作は原理的にカバーできない
- htmxに詳しい人がいるなら、CLAUDE.mdにルールを追記するのも有効だが、それは前提知識ありきの対策
バグ② タグ検索の#問題 — これは「バグ」ではなく「仕様の穴」
厳密にはバグではない。DBのタグ名は # なしで保存、テンプレートで # を付けて表示、検索は完全一致 — それぞれの実装は仕様通りで正しい。問題は 「ユーザーが画面に見えている通りに入力したら動かない」 というUXの不整合であり、設計書にタグの表示形式と検索入力の関係が明示されていなかったことが根本原因。# 付き表示と検索フォームの入力仕様が別々のタスクで実装されたため、整合性の検討が漏れた。
加えて、カンマ区切りの複数タグOR検索はセッション中にユーザーから出た要求追加であり、元の実装計画には「タグ1つで絞り込み」としか書かれていなかった。
防ぐには:
-
EARSユースケースでUI操作の流れを端から端まで記述する。 「ユーザーがタグ
#初心者歓迎をクリックしたとき、そのタグを持つコミュニティが検索結果に表示される」のように、表示→入力→検索の一連の流れをユースケースに含めていれば、表示形式と検索ロジックの不整合に気づけた -
テストデータに境界値を入れる。
#付き入力のテストは最初から書けたはず。実装計画のテスト項目に「表示形式と入力形式の差異」を意識した項目があれば防げた - ただし、複数タグOR検索のような要求追加は防ぐ対象ではない。 使ってみて初めて出てくる要望であり、これは対話ベースの開発が正しく機能した例
バグ③ display_nameが空のユーザーが見えない
原因: Django の createsuperuser コマンドで管理者アカウントを作成した際、display_name が入力されなかった。CustomUser モデルは display_name を default="" で定義しており、createsuperuser の REQUIRED_FIELDS にも含まれていないため、空のまま作成される。テンプレートが {{ m.user.display_name }} を直接参照していたため、空文字=何も表示されないという結果になった。
つまり、AIが仕様を知らなかったのではなく、createsuperuser の挙動とテンプレートの表示ロジックの組み合わせが考慮されていなかった。 __str__ にはフォールバックがあるのに、テンプレートがそれを使っていない、という不整合。
防ぐには:
-
コーディング規約にルールを追加する。 「テンプレートでユーザー名を表示する場合は
{{ user }}(__str__)を使い、{{ user.display_name }}を直接参照しない」。__str__にフォールバックを持たせた設計意図がテンプレート側に伝わっていなかった -
テストデータに空のdisplay_nameを含める。 conftest.pyのfixtureで
display_name=""のユーザーを作り、一覧表示系のテストに含めていれば検出できた -
REQUIRED_FIELDSにdisplay_nameを含める。 そもそもcreatesuperuserで入力を求める設計にしていれば、空のユーザーは作られなかった。ただしこれはモデル設計の判断であり、空を許容する設計自体は間違いではない
共通の教訓
| 対策 | バグ①に効く | バグ②に効く | バグ③に効く |
|---|---|---|---|
| 実装計画・設計書の精度を上げる | △(知識が必要) | ✅ | |
| CLAUDE.md / コーディング規約にルール追加 | △(知識が必要) | ✅ | |
| EARSユースケースの充実 | ✅ | ||
| テストデータの境界値・エッジケース | ✅ | ✅ | |
| E2Eテスト(Playwright等)の導入 | ✅ |
バグ①が示すのは、ライブラリ固有の知識がないと、ドキュメントを充実させるアプローチ自体が成立しないケースがあるということ。htmxのswapモードの落とし穴は、htmxを知らない人には規約にもレビューにも書けない。このようなケースでは、知識に依存しない 振る舞いベースのE2Eテスト が唯一の現実的な防御線になる。
結論: AI自律実装の品質は 「実装計画」「コーディング規約」「ユースケース」の3つの入力ドキュメントの精度 にほぼ比例する。AIは指示されたことは正確にやるが、指示されていない暗黙の期待(UIとロジックの整合性、空データのフォールバック)は見落とす。そして、ライブラリ固有の落とし穴のようにドキュメントでカバーしきれない領域には、E2Eテストで網をかける。 人間がレビューすべきはコードよりも「入力ドキュメントの網羅性」と「テスト戦略の妥当性」 である。
所感
-
自律実装(セッション①)は驚くほど手がかからない。 14タスク・75テスト・15コミットが、ユーザーの介入ゼロで完了した。実装計画(
docs/plans/current.md)の精度がそのまま品質に直結する -
手動テストで初めて見つかるバグがある。 htmxの
hx-swapパターンミスはテストだけでは検出しにくい。自動テスト + 手動確認の組み合わせが重要 - 対話で仕様が段階的に明確化される。 編集機能では「statusをpendingに戻す → 参加できなくなる → EditRequestモデルに方針転換」と、ユーザーのフィードバックで設計が洗練された。これはAI単独では到達しにくい
- subagent-driven-development は「実装」だけでなく「状況確認」にも使える。 残タスク確認やプロジェクトの現状把握にも有用
