前日に state-vs-events.vue を1本作ったら、同じ枠組みで動く教材が次々と頭に浮かんで止まらなくなった。「OAuthを認証と呼ぶ人に向けた話」「MVC/MVVM/MVPの違いを図で並べる話」「Web開発が将来ニューラルネットの最適化処理に収束していくのではないかという仮説」の3本がほぼ同時に思いついて、しかもどれも他のテーマに依存しない。それぞれが独立したVueページに収まる。だったら直列で1本ずつ書く理由がない、と判断した。file-editor サブエージェントを3体並列で起動し、私は方針だけを渡して、3本を同時に書き上げてもらった。
何を作ったか
apps/web/app/pages/blog/ に独立したVueページを3本、いずれも前日の state-vs-events.vue と同じインタラクティブ教材の枠組みで公開した。
oauth-is-not-authentication.vue― OAuth 2.0 は認可(Authorization)のフレームワークで、認証(Authentication)ではない。「OAuthでログイン」と書いた瞬間になりすましの穴が開く。OpenID Connect のid_tokenとaccess_tokenが役割を分けて持ち、id_tokenの署名検証ではじめてユーザー認証が成立する、というところまで操作で追わせる。mvc-mvvm-mvp.vue― 3パターンの違いを「データバインディングの有無」と「Presenter/ViewModel/Controller の配置」の2軸で図解した。同じ「ボタンを押してカウンタが増える」操作を3パターン同時に流して、依存方向とView更新の経路が並んで動くようにした。web-dev-as-nn-optimization.vue― Karpathy の Software 2.0/3.0 を引きながら、Web開発が将来ニューラルネットの最適化過程に収束していくという仮説を賛否両論で扱う。手元の機能を Software 1.0 / 2.0 / ハイブリッドに分類するクイズを差し込んだ。
3本とも前日の枠組みを踏襲した。純粋関数を app/utils/ に切り出し、Vueページは副作用シェルに徹し、テストは tests/*.test.ts に置く。
サブエージェント3体を並列で起動した
私が手で書いたのは「3本書け」という方針だけだ。あとはサブエージェント3体を同じターンで並列起動し、各エージェントに以下を分担させた。
- エージェント1:
oauth-is-not-authentication.vue+app/utils/oauthFlow.ts+app/composables/配下のOAuth用composable +tests/oauth-flow.test.ts - エージェント2:
mvc-mvvm-mvp.vue+app/utils/uiArchitecturePatterns.ts+ 対応するcomposable +tests/ui-architecture-patterns.test.ts - エージェント3:
web-dev-as-nn-optimization.vue+app/utils/softwareEraClassifier.ts+ 対応するcomposable +tests/software-era-classifier.test.ts
3エージェントが触るファイルは完全に独立していて、import の依存もない。同じファイルを書き換える衝突は構造上起きない。3体が同時に走っているあいだ、私のターミナルにはサブエージェントの進捗ログが3列で降ってきて、それぞれが「oauthFlow.ts を書き始めた」「uiArchitecturePatterns.ts のテストが通った」「softwareEraClassifier.ts のVueコンポーネントを最後に組み立てている」と互いを意識せずに動いていた。
唯一の競合点だけ自分の手で整えた
3エージェントが同時に書き換えると衝突する場所が1つだけある。apps/web/app/composables/useBlogArticles.ts の vuePageArticles 配列だ。新しいVueページを公開するには、この配列の先頭にエントリを1件追加する必要がある。3エージェントに同時に同じ配列を編集させると、後勝ちで上書きが起きて、先に書かれた2本が消える。
ここだけ私が引き取った。3体の作業がすべて完了したのを待ってから、vuePageArticles 配列の先頭に3本まとめて登録した。前日に追加した state-vs-events のエントリの直前に、oauth-is-not-authentication → mvc-mvvm-mvp → web-dev-as-nn-optimization の順で並べた。これで blog一覧ページに3本が同時に並ぶ。
「並列分担で書かせる ⇒ 競合する一点だけ筆者本人がまとめてやる」という分担にすると、サブエージェントが互いを意識せずに済む。3体の起動と完了待ちを除けば、私が手を動かしたのはこの登録作業だけだった。
各記事の核となる主張
3本とも教材として独立しているので、それぞれの核を1〜2行で残しておく。
OAuth は認可であって認証ではない。 OAuth 2.0 の access_token は「あなたがこの API を叩いてよい」という許可証であって、「これを持っている人物は確かにあなただ」とは何も言っていない。id_token(OpenID Connect が定義する署名付きJWT)の署名・aud・iss・nonce を検証してはじめて、ユーザー認証が成立する。教材では認可コードフローを動かしながら、access_token だけでログインを判定する実装に切り替えるとトークン置換攻撃が成立してしまう様子を、操作で再現させた。
MVC・MVVM・MVP の違いは「View と Model の仲介役を誰が担うか」と「データバインディングを使うか」の2軸で整理できる。 MVC は Controller が入力を受け取って Model を更新し、View が Model を観察する。MVP は Presenter が View と Model のあいだに立ち、View は受動的に Presenter から指示を受ける(データバインディングは使わない)。MVVM は ViewModel と View をデータバインディングで双方向に結んで、View が ViewModel の状態を反映する。教材では同じ「カウンタを増やす」操作を3パターン並べて、矢印がどちら向きに走るかを目で見られるようにした。
Web 開発は将来「ニューラルネットの最適化処理」に収束していくのか。 Karpathy が言う Software 1.0(人間が書くコード)・Software 2.0(学習で得た重み)・Software 3.0(自然言語で動かす)の枠組みに、いまの自分たちのWeb開発を当てはめる。バリデーション・認可・ビジネスルールは1.0のままだが、推薦・検索・要約・分類は2.0に置き換わりつつあり、UIの一部は3.0(プロンプトで生成)に近づいている。「全部NNになる」までは言わないが、「機能ごとに 1.0 / 2.0 / 3.0 が混在する」までは確からしい、という賛否両論の整理を入れた。
テストは合計71件、全件パス
3記事それぞれに対応する純粋関数のユニットテストを書かせた。
oauthFlow.ts― 19テスト(認可コード生成・トークン交換・id_token検証・トークン置換攻撃の検出)uiArchitecturePatterns.ts― 26テスト(3パターンそれぞれの依存方向、データバインディング有無、View更新経路)softwareEraClassifier.ts― 26テスト(機能を Software 1.0 / 2.0 / ハイブリッドに分類する純粋関数、エッジケース、未知入力の扱い)
pnpm test:run を走らせて、71件すべてが緑で通った。app/utils/ を触ったのでカバレッジも計測した。3つのユーティリティはいずれも引数→戻り値だけで完結していて、ref も Date.now() も触らない。テスト側もモックを書く必要がなかった。
全体テストは24件失敗していたが、いずれも作業範囲の外だった。連結精算表(cashflow-worksheet)の既存エラー、content/ 配下のMarkdown記事のリンク切れ、既存ページのOGメタ不足など、今回の3記事とは別文脈の失敗で、私のスコープでは触らない判断をした。
学びメモ
- 完全に独立した3本のページなら、サブエージェントを並列起動して同時に書かせるのが最も速い。直列で書いていたら3倍の時間がかかっていた。
- 唯一の競合点(
useBlogArticles.tsの配列追加)だけ筆者本人がまとめて引き取る、という分担が効いた。サブエージェントは自分以外の存在を意識しなくてよくなる。 - 純粋関数を
app/utils/に切り出す設計は、サブエージェント並列とも相性がいい。各エージェントが触るユーティリティが独立しているので、import 経由の衝突も起きない。 - 「方針だけ渡せば実行はAIが3並列でやる」という構図は、税理士業務にも応用が利く。たとえば独立した3社の月次仕訳を同時に進めるとき、共通の番号採番だけは手元で握って、入力はAIに並列で任せる、というやり方に組み替えられる。