会場別のコース適性を分析する。会場を省略すると全会場の成績を表示する。「コース分析」「会場別の適性を見たい」といった依頼で発動する。
review-recovery
再実行性・リカバリー・テスタビリティのレビュー。
new Date() / Date.now() の直接呼出と時刻の注入可能性、toISOString() が UTC 日付になる点、同じ JSON を2回取り込んでも重複行・統計の二重計上が起きない再インポートの冪等性(RebuildStats との関係)、トランザクション境界と途中失敗時の一貫性、DB 接続のクローズを検証。
Use when user says 'リカバリーレビュー', '再実行性チェック', 'review recovery', '冪等性確認', or 'テスタビリティ確認'.
含まれるファイル(1)
- SKILL.md15.0 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Instructions
src/ 配下の TypeScript と src/database/schema.sql を探索し、以下のチェックリストに基づいてレビューする。このプロジェクトは CLI であり、同じコマンド(特にインポート)が何度も流されることが前提。
対象スコープと指摘範囲
起動時に「対象スコープ」と「対象ファイル一覧」が渡される場合がある(review-all 等からの dispatch 時)。その扱いは次の通り:
- 探索は広く、指摘は狭く: 文脈把握のための Glob/Grep/Read はプロジェクト全体に対して行ってよい。ただしテーブルに載せる WARN/FAIL は対象ファイル一覧内の事象に限定する
- 該当ファイル不在の項目: 評価対象が無い項目は OK として計上しない(テーブルにステータス
N-Aで残し、備考に根拠を書く。行を省かない) - 自観点の評価対象が皆無: 判断根拠 1 行 +
Summary: SKIPPEDの最小出力でよい(テーブル骨格は不要) - スコープ未指定時: プロジェクト全体(
node_modules/は除外)を対象にする - 一覧に実在しないファイルがある場合: 除外して続行し、詳細所見に 1 行明記する
- 既存コードの違反(差分外・本変更で未変更の行)は既存債務として扱い、FAIL に上げず WARN 以下で備考に「既存債務(本変更非起因)」と明記する
対象ファイルの探索
- Grep:
new Date(/Date.now()/getFullYear()/toISOString()(__test__を除く本番コードを対象に、件数のベースラインをその場で取る) src/commands/ImportData.ts(取り込みの本体・トランザクション・統計再構築の呼び出し)src/repositories/aggregates/(insertRace/insertHorseWithBloodline/insertRaceEntryの一致条件と upsert、ScoreAggregateRepository.rebuildHorseStats)src/database/DatabaseConnection.ts(runInTransaction/close)とsrc/database/schema.sql(UNIQUE 制約・ON CONFLICTの効き方・CURRENT_TIMESTAMP)src/commands/各コマンドのfinallyでの接続クローズ、src/models/MachineLearningModel.tsのownsConnection方式
チェックリスト
7.1 システム時刻の直接取得が業務データ・業務判定に使われていないか
-
src/domain/内の引数なしnew Date()/Date.now()は pluginno-ambient-time(warn・src/domain/**のみ適用)が機械検出する。該当 warning が本変更で純増していないかを見る(ドメインは純粋に保ち、必要な日付はレース日・基準日として引数で受け取る) - 保存されるデータの値(生年・レース日・年)を実行時の現在時刻から導いていないか。同じ入力を後日もう一度流すと違う値が入るなら再実行非決定性
- 現在時刻が必要な箇所は、テストから差し替えられる形(引数・オプション・
now: () => Dateのようなフィールド)になっているか。形は問わずテストから固定できることが基準 - ログ・メタデータ用のタイムスタンプ(抽出時刻、
schema.sqlのcreated_at DATETIME DEFAULT CURRENT_TIMESTAMP/updated_at)は業務判定に使わない限り直接取得でよい(指摘対象外。備考に区別を書く) - 「現在時刻フォールバック」を足す場合、それが黙って誤った値を入れないか(失敗として扱う/警告を出す選択肢を検討したか)
NG例(現状 — 既存債務): ImportData の生年計算 — const baseYear = raceYear ? Number(raceYear[1]) : new Date().getFullYear();。レース日から年が取れないとき実行時の年で生年を計算するため、過去データを後日再インポートすると birth_year がずれる。HorseDataExtractor も同様で、URL から日付が取れないとき new Date().toISOString().split('T')[0] をレース日の既定値にしている
OK例: Horse.calculateRotationAptitudeScore — 出走間隔をレース日同士(current.raceDate と prev.raceDate)の差分で求めており、現在時刻に依存しない
7.2 日付の UTC / JST
-
toISOString()の結果から日付部分を切り出していないか(toISOString()は UTC。JST の 0:00〜8:59 は前日の日付になる) - 日付文字列の生成・比較・キーが同じ基準(
YYYY-MM-DDのレース日)で揃っているか - 日付の比較を文字列のまま行う箇所で、形式(ゼロ埋め・区切り)が揃っているか
-
new Date('YYYY-MM-DD')のパース結果(UTC 起点)で日数差を取る場合、両辺が同じ扱いで打ち消し合うか(片側だけローカル時刻由来だとずれる)
NG例(現状 — 既存債務): HorseDataExtractor のレース日フォールバック new Date().toISOString().split('T')[0] は UTC の日付。日本時間の早朝に実行すると前日の日付になる
OK例: レース日は URL から抽出した YYYY-MM-DD をそのまま使い、実行環境のタイムゾーンを介在させない
7.3 再インポートの冪等性
同じ JSON をもう一度取り込んでも結果が同じになることがこのプロジェクトの中核要件:
- 馬・レース・出馬表・レース結果の一致条件(馬=馬名+父+母、レース=開催日+会場+レース番号、出馬表=レースID+馬ID、レース結果=エントリID)に従い、再取り込みで行が増えず更新になるか
- 一致条件に使う列に UNIQUE 制約/
ON CONFLICTがあり、アプリ側の SELECT→INSERT と DB 側の制約が食い違っていないか - 成績の集計(
horse_course_stats/horse_track_stats)が加算ではなく再構築か。加算で更新すると再取り込みのたびに出走数・勝利数が二重計上される - 取り込み後に
rebuildHorseStats()を呼ぶ順序が保たれているか(前走データの取り込みより後か)。再構築を外す・条件付きにする変更は二重計上や集計漏れを招かないか - 新しいテーブル・集計を足すとき、再取り込み時の扱い(upsert か再構築か)を決めて実装しているか
- 途中でエラーになった取り込みをやり直したとき、前回の部分的な書き込みが残って矛盾しないか
NG例: 統計を UPDATE horse_course_stats SET runs = runs + 1 ... のように加算で更新する(同じ JSON を2回取り込むと出走数が倍になる)
OK例: ScoreAggregateRepository.rebuildHorseStats — DELETE FROM horse_track_stats / DELETE FROM horse_course_stats してから保存済みのレース結果を正として再構築し、ImportData がインポートの最後に必ずそれを呼ぶ
7.4 トランザクション境界と途中失敗時の一貫性
- まとめて成立すべき書き込みが 1 つのトランザクションに入っているか(
DatabaseConnection.runInTransactionまたはdb.transaction(...)) - トランザクションの外に出している処理があるなら、その理由と「失敗しても本体が壊れない」根拠がコメントに書かれているか
- トランザクション内で例外を握り潰していないか(握り潰すとロールバックされず、部分的に書かれた状態で成功扱いになる)
- 失敗をスキップして続行する設計(前走データのスキップ等)で、スキップ件数が出力に出るか(黙って減らない)
- トランザクションの中でネットワーク取得・巨大ループなど長時間の処理を行っていないか(ロック保持時間)
- 統計の再構築(
DELETE→ 再 INSERT)が 1 トランザクション内で完結し、途中失敗で統計が空のまま残らないか
NG例: catch (e) { console.warn(e); } をトランザクション内の各行の書き込みに付ける(一部だけ入った状態でコミットされ、次回の再取り込みでも一致条件に引っかかって直らない)
OK例: ImportData.importExtractedJSON — レース・馬・出馬表の登録を db.transaction(...) にまとめ、前走データだけは意図的にトランザクション外に出し、「前走データのエラーがメインのインポートに影響しないように分離」とコメントで根拠を残したうえでスキップ件数を集計している
7.5 DB 接続・リソースの解放
- コマンドが自分で開いた接続を
finallyで必ず閉じているか(例外時も閉じるか) - 外から接続を渡された場合は閉じないか(所有権の区別。
ownsConnection相当のフラグがあるか) - テストが作る一時 DB が必ず削除されるか(
createTestDb(...).cleanup()) - 接続を閉じた後にリポジトリを使い続けていないか(長寿命のオブジェクトが閉じた接続を握っていないか)
- 同じコマンド内で複数の
DatabaseConnectionを開いていないか(new DatabaseConnection()はスキーマ適用も走るため、無駄な再初期化になる)
NG例: try の最後で connection.close() を呼ぶ(途中で例外が出ると閉じられず、SQLite ファイルのハンドルが残る)
OK例: ImportData / CalculateScore / Predict / RebuildStats の finally { this.connection.close(); }。外部接続を受け取る側は MachineLearningModel のように ownsConnection を見てから閉じる
判定基準
- OK: 当該項目が判定する対象が差分にあり、期待通り実装されている(適合の積極的根拠がある)。「違反が無いから OK」ではなく「該当する対象があって適合している」ときに OK を付ける
- WARN: 動作上は問題ないが改善余地あり(現在時刻フォールバックが残る、トランザクション外処理の根拠がコメントだけ、既存債務の踏襲)
- FAIL: 保存値を実行時の現在時刻から導く新規実装、再取り込みで行が増える/統計が二重計上される実装、まとめるべき書き込みのトランザクション外出し、例外時に閉じられない接続、UTC 由来で日付がずれる業務判定
- N-A: 該当する実装が差分スコープに存在せず評価不能(備考に根拠を記載)。差分外の既存実装は「OK(既存)」とせず N-A にし、「違反が無い」だけで OK にしない。複数サブチェックを持つ項目(例 7.1・7.3)は、いずれかのサブチェックに該当する対象が差分にあればその適否で OK/WARN/FAIL を付け、どのサブチェックの対象も差分に無ければ N-A とする
SKIPPED 条件
- 時刻・取り込み・DB 書き込み・トランザクション・接続管理のいずれにも関わらない変更(表示のみ、スコア計算式のみ、ドキュメントのみ等)の場合
Summary: SKIPPED
本スキルの責務境界
- スコアリング / ML の計算自体の決定性(学習・予測の再現性、正則化パラメータ等)は review-scoring、取り込むデータの検証は review-security、テストの書き方は review-test、層配置は review-arch、エラーハンドリングのコード品質は review-code。本スキルは「時刻の注入可能性・日付基準・再実行時の冪等性・トランザクション・リソース解放」のみ
- チェック表の行は SKILL.md のチェックリスト項目のみで構成する。チェックリストに無い独自の行を表に追加しない(チェック項目外の気づきは「他観点への申し送り」へ。自観点に関連するが項目化されていない所見は「詳細所見」に書き、Summary には数えない)
- 表には全チェックリスト項目(7.1〜7.5)を採番項目単位で列挙する(チェックボックスごとに a/b/c へ分割しない)。該当変更が無い項目も N-A 行として残し省略しない(レビュー網羅性の担保)。Summary には OK/WARN/FAIL の行のみ計上し N-A は除外する
- 同一の行・欠陥でも、自観点のチェックリスト項目に明示的に該当する側面だけを自分の表・Summary に計上する(多面評価は可)。チェックリストの項目名から読めない拡大解釈(docs や一般原則からの引き込み)で FAIL を増やさない
- 自観点の項目に該当しない問題は計上せず、レポート末尾の「### 他観点への申し送り」節に「対象スキル名: 1行説明」で記載するだけにする(FAIL/WARN を付けない)
- 同一事象が自観点の複数チェック項目に該当する場合は、最も特異的な 1 項目だけで計上し、他項目は備考で相互参照する(Summary で重複カウントしない)。特異性の判定: 違反の根本原因を最も具体的に名指す項目を選ぶ(例: 現在時刻から生年を決める事象は『システム時刻の直接取得』7.1 が根本で、結果として再取り込みで値が変わる 7.3 は備考)。優劣が付けにくいときはチェックリスト番号の小さい方で計上する(決定論的に揃える)
- 差分内コードが依存する差分外の既存実装は、確認してよいが新たな指摘対象にはしない(当該チェック項目を OK とし、備考に「依存先は差分外・確認済み」と書く)
レポート形式
## リカバリー / テスタビリティ レビュー結果
| # | チェック項目 | ステータス | 該当箇所 | 備考 |
| - | ------------ | ---------- | -------- | ---- |
### 詳細所見
### 推奨アクション
- HIGH / MEDIUM / LOW(該当が無い優先度は「なし」と明示)
Summary 行(必須・review-all 集計用)
レポート末尾に必ず以下の 1 行を出力する:
Summary: OK=<n> WARN=<n> FAIL=<n>
- カウントは N-A を除いたチェック項目テーブルの行数と一致させる。レビュー対象不在時のみ
Summary: SKIPPED
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
抽出済みJSONファイル(馬・血統・レース出走データ)をSQLiteデータベースにインポートする。「DBインポート」「データベースに保存」「JSONをDBに登録」といった依頼で発動する。
実装前に参照する設計原則。理解容易性 = 読み手の思考量の少なさを基準に、AI が作りがちな失敗4パターン(引数・依存5個以上、コマンド層肥大・ドメイン貧血症、ポリモーフィズム機会の見逃し、トリッキーな実装)と、実装後セルフチェックの7観点(名称・役割・参照・状態・面積・階層・秩序)を言語化。Use when starting any implementation task, when user says '設計原則', 'design principles', or before writing new entities/commands/repositories.
agent 向けテキスト指示(skill / slash command / task プロンプト / コード生成プロンプト)を、バイアスを排した実行者に動かしてもらい、両面(実行者の自己申告 + 指示側メトリクス)で評価して反復改善する手法。改善が頭打ちになるまで回す。Use when user says 'プロンプトを改善して', 'スキルをチューニング', 'empirical-prompt-tuning', or after creating/heavily revising a skill or prompt.
JRA公式サイトから特定レースの出馬表HTMLを取得して馬データを抽出する。「データ取得」「JRAからデータを取ってきて」「出馬表を取得して」といった依頼で発動する。
有馬記念分析システムで利用可能なスキル一覧と使い方、スコア配分、基本ワークフローを表示する。「ヘルプ」「使い方を教えて」「何ができるの」といった依頼で発動する。