会計システムのBS/PL実装から年度繰越まで、Codexレビューで固めたPhase4・Phase5開発

開発accounting-system-lab

前回の続きを確認するところから

朝一で「多分途中だったと思うので、ちょっと進捗確認してくれませんか」とだけ伝えて、会計システムラボの状態をClaude Codeに洗い出させた。ユニットテストは26件全部通っている。開発サーバーを起動させたら302が返ってきて一瞬ログインリダイレクトかと身構えたが、原因は別にあった。本来使うべき開発用DBではなく、E2Eテスト用の未マイグレーションDBを指していたのだ。DB_URLを直させて再起動すると、ADMIN権限でログインした状態のナビゲーションがちゃんと現れた。

進捗はこうだった。基盤・マスタと仕訳ワークフローはコミット済みで動いている。残っていたのは総勘定元帳と試算表、そしてその先の財務諸表だ。

総勘定元帳・試算表を仕上げてPhase4へ

コミットを指示すると学習ゲートに止められた。ステージされていたのはPlaywrightのタイムアウト設定だけだったが、直感優先の解説とクイズの流れに乗せると4問中2問を落とした。reuseExistingServerの挙動――「既存サーバーと新規プロセスがランダムに振られる」のではなく「既存があればそのまま使い、新規プロセスは起動しない」――を解説してもらってから再挑戦し、全問正解で受領証をもらってコミットした。

続けて総勘定元帳と試算表の実装に入らせた。ドメインロジックのテスト、期間セレクト用API、コンポーザブル、ナビリンクと積み上げ、ユニットテスト37件・型チェックまで通したところでブラウザ確認に回した。試算表は借方10,000円・貸方10,000円で貸借一致、現金科目の元帳明細も相手科目まで正しく解決されている。E2Eも既存分が壊れていないことを確認してコミット、これで一区切りだと思っていた。

ところが「ごめん、表示確認してほしいんですけど」と言われて見直すと、期間セレクトのドロップダウンで終了日が見切れていた(「第1期間(2026-01-01〜2026-01...」)。トリガー自体を広げるのではなく、閉じた表示はコンパクトなまま、開いたときのドロップダウン幅だけ内容に合わせて広げる形に直させた。これも学習ゲートを通してコミット。

次は財務諸表(BS/PL)だと決めて、会計ソフトAの試算表・貸借対照表・損益計算書の画面を一通り見て回らせた。貸借対照表は資産の部と負債・純資産の部を左右2カラムに並べる構成、損益計算書は縦1列で小計を挟みながら積み上げていく構成。これは既存のスキーマ設計(GROUP/ACCOUNT_SUM/FORMULAのノードを木構造で持つ)とちょうど噛み合う形だとわかり、そのままBS/PL実装の計画に入った。ここでコンテキストが厳しくなり、次セッションへの引き継ぎプロンプトを出させて区切った。

Phase4: セキュリティレビューの指摘を先に飲み込む

次のセッションは「Phase4作業を続けます」の一言から再開した。実は前のセッションを閉じた直後、コミットのたびに自動で走るセキュリティレビューが、試算表クエリにcompanyIdによるテナントスコープが入っていない点を指摘していた。MVPは単一テナント運用だが、防御的に直しておくのが筋だと判断してまず反映させ、そのうえで試算表APIを共通関数呼び出しにリファクタリングさせた。

続けてBS/PLのドメインロジック、API、コンポーザブル、ページ、ナビリンクと一気に積ませ、ユニットテスト44件・型チェック・E2Eまで通し、ブラウザで最終確認した。資産合計と負債・純資産合計が7,810,000円で一致、現金残高も試算表と一致、PLの当期純利益10,000円もBS側の数字と揃っている。コンソールエラーもない。

学習ゲートのクイズは4問中3問正解。落としたのは「BSとPLでaccountAmountsの意味が違う」設問で、PLの勘定科目に期首残高投入を禁じるDB制約がある、という誤答だった。実際にはそんな制約はなく、closingBalance - openingBalanceで差分を出しているのは、PLが期間中の増減(フロー)を表す帳票で、BSがある時点の残高(ストック)を表す帳票だから、という会計上の意味の違いを反映しているだけだった。クイズにはBS/PLの構造図も添えられていて、GROUPノードがどう再帰集計されるか、符号反転がどこで効くかを図で追いながら解説を受けた。

もう一度出題されたところで「ごめん、もうテストパスでいいです」と伝えたら、issue_receiptは実際に正解したセッションでないと発行できないので強制合格はできない、LEARN_SKIPで理由を残す形にするか設問を言い換えて再挑戦するか、と聞き返された。理由を残してスキップすることに決め、Phase4をコミット。ついでに開発サーバーのデフォルトポートを3300に変更したが、これは学習価値のない機械的な設定変更だと判断してこちらもLEARN_SKIPにした。

これで最初の計画書の内容はすべて完了していた。

Phase5: 年度繰越の安全性でCodexと5往復

「次のフェーズってなんかあるんでしたっけ?」と聞くと、ロードマップにPhase1〜6の全体計画がちゃんと残っていて、次は月次推移表・部門別P/L・年度繰越だとわかった。「じゃあ次進んでくださいよ」で計画モードに入り、調査エージェントを2体並列で走らせて素材を集めさせた。

計画が10ファイルを超える規模になったので、プロジェクトのルールに従ってCodexにレビューさせた。1回目で年度繰越機能に致命的な指摘が3点。直して2回目に出すと、今度は競合のレースコンディション・繰越元年度の期首残高保護漏れ・未承認仕訳の取りこぼしと、また3点返ってきた。ここまでの修正でデータ破損リスクの主要な部分は塞げていたが、3回目のレビューでCodexはさらにPhase2側(仕訳の新規作成・編集・提出API)にも同じレース対策を求め、DBトリガーと二接続を使うDB統合テスト基盤の新規構築まで要求してきた。すべて飲むとPhase5の規模が実質2倍近くに膨らむ。折り合いをつけて中間案で計画を固め、Phase2側の横展開対象4ファイルだけ追加する形にした。

4回目のレビューでは処理順の記述ミスを直し、5回目でE2Eテストの状態分離漏れ(既存テストと同じDB・同じ年度を共有してしまう設計)を指摘され、E2E専用の会計年度を切り替えて衝突を避けた。5回のレビューを経てようやく計画が固まり、承認をもらって実装に入った――ところがStep1の試算表ロジックを読んだ直後にコンテキストが逼迫し、コードはまだ一行も書かないまま次セッションへの引き継ぎプロンプトを出させた。

実装してみて初めて見つかる設計の穴

月次推移表(Step1)はドメイン関数を積んでユニットテストを通し、ブラウザで確認した。curlがWSL越しでうまく届かない場面があったが、Chrome DevTools経由でIPv6ループバックからアクセスさせて200を確認できた。

部門別P/L(Step2)は既存の財務諸表APIから共通ロジックを切り出させ、部門を切り替えると金額がちゃんと変わることを確認した。

年度繰越(Step3)が一番重かった。仕訳4APIの書込みトランザクション内に会計期間の再検証を入れさせ、期首残高更新APIにADMIN権限チェックとCLOSED年度・繰越済み年度への保護を追加させた。実行してみると、当期に未承認の仕訳が2件残っていて先に進めなかったので承認し、さらに検証中に見えていなかった設計の穴が出てきた。勘定科目の有効開始日が固定されていて、当初想定していた過去年度では仕訳計上そのものができない。E2E用の会計年度を振り直して回避した。実際に手を動かしてみるまで見えない類の不具合だった。

E2Eは既存2件・新規4件の全6件が通り、型チェック・ユニットテストも問題なし。学習ゲートは5問全問正解で受領証を発行してもらい、コミット。これでPhase1からPhase5まで、MVPが一通り形になった。

「特にバグとかも発見してないんでしたっけ?」と聞き返してみると、ユニットテストもE2Eもブラウザ確認も「書いたものが仕様通り動くか」の確認であって、「見落としがないか」を疑ってかかるレビューは一度もやっていない、という答えが返ってきた。バグ探し専用のコマンドがあったはずだと思い出し、/code-reviewの存在を教えてもらった。コンテキストも厳しくなってきていたので、ロードマップに進捗を追記させ、次セッションへの引き継ぎプロンプトを出させてからこのセッションを区切った。

Phase5レビュー: 並列で走らせた3体が拾ったTOCTOU漏れ

次のセッションで/code-reviewを直接呼ばせようとしたら、これはモデルから起動できないユーザー専用コマンドだった。代わりにコードレビュー用のエージェントを3体、Claude Codeにバックグラウンドで並列に走らせた。観点は「年度繰越APIの安全性」「TOCTOU対策の横展開」「月次推移表・部門別P/Lの集計ロジック」の3つ。

先に返ってきたTOCTOU対策の横展開レビューで、重要な指摘が1件見つかった。仕訳のPATCH・提出・承認の各エンドポイントは会計期間のOPEN/CLOSEDをトランザクション内で再検証していたが、仕訳自体のステータス(下書き・提出済み・確定済み)はWHERE句に入っていなかった。バージョン管理用のカラムも用意はされているのに使われていない。二重送信や同時承認、遅延したPATCHが重なると、確定済みのデータが後から来たリクエストに黙って上書きされる可能性がある――確信度88という評価だった。集計ロジックのレビューは計算自体に問題なし、テストカバレッジの不足だけが指摘として残った。

5件の指摘のうち確信度80以上の4件を直すことに決めた。DBクライアントに待機タイムアウトを追加させ、仕訳4エンドポイントにCAS(Compare-And-Swap)方式の状態チェックを足させ、同じ穴を持つ却下エンドポイントにも同じ修正を横展開させた。年度繰越の一意制約もキーを組み直してマイグレーションを適用、UI側の年度セレクトも合わせて直した。テスト追加だけは今回見送った。

型チェック・ユニットテスト・E2E6件をすべて通し、既存タブを邪魔しないよう新規タブでブラウザ確認させ、年度繰越画面も仕訳画面もコンソールエラーなしで表示されているのを見てから、ロードマップを更新させた。学習ゲートは「スキップでいいんで」の一言で理由を残してスキップし、コミット。Phase4の財務諸表実装からPhase5の年度繰越の安全性強化まで、計画・実装・レビュー・修正の一周が形になった一日だった。