会場別のコース適性を分析する。会場を省略すると全会場の成績を表示する。「コース分析」「会場別の適性を見たい」といった依頼で発動する。
design-principles
実装前に参照する設計原則。理解容易性 = 読み手の思考量の少なさを基準に、AI が作りがちな失敗4パターン(引数・依存5個以上、コマンド層肥大・ドメイン貧血症、ポリモーフィズム機会の見逃し、トリッキーな実装)と、実装後セルフチェックの7観点(名称・役割・参照・状態・面積・階層・秩序)を言語化。
Use when starting any implementation task, when user says '設計原則', 'design principles', or before writing new entities/commands/repositories.
含まれるファイル(1)
- SKILL.md12.8 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
位置づけ
本スキルはコードを書く前と書いた直後に読む設計原則。レビュー時の機械的検出は Biome(bun run lint)と review-code / review-arch / review-scoring が担い、本スキルは「そもそもそう書かない」ための判断基準を言語化する。
規約の正は docs/ARCHITECTURE.md(層構成・スコアリング/ML パイプライン)と docs/DEVELOPMENT.md(技術スタック・命名・コーディング規約)。本スキルはその2つを前提に、判断の物差しだけを与える。
物差しは一つ: 理解容易性 = 読み手の思考量の少なさ。「初見の人間が上から読んで、追加の思考をどれだけ強いられるか」で書き方を選ぶ。短さ・巧さ・抽象度の高さは、それ自体では価値ではない。
第1部: AI が作りがちな失敗4パターン(書く前に問う)
原則 1: 依存・引数の凝集 — 5個は分割のシグナル
コンストラクタ引数・メソッド引数・オプションオブジェクトの項目が 5個以上になったら、追加する前に立ち止まる。多引数は「このクラスが複数の責務を抱えている」か「引数の一部が概念としてまとまりたがっている」かのどちらかのシグナル。
対処は追加ではなく再編:
- 引数の一部が常にセットで使われる → その組に名前を付けて値オブジェクトに束ねる(実例: 10個のスコア要素は個別の number として持ち回さず
ScoreComponents(src/domain/valueObjects/ScoreComponents.ts)に束ねてある) - 組み立てに必要な材料が多い → ビルダーで段階的に組む(実例:
Horse.builder(id, name).withDetail().withRaceResults().withCourseStats().withTrackStats().build()。src/domain/entities/Horse.ts) - 依存の一部が特定メソッドでしか使われない → そのメソッド群ごと協力クラスに切り出し、依存を分配する
安易な逃げ(オブジェクト1個に全部詰めて実質同じ・any 化)は不可。「数を減らす」のではなく「凝集を上げる」のが目的。
原則 2: ロジックの置き場所 — commands はオーケストレーションのみ
commands 層(CLI コマンド)が肥大し domain が貧血症になるのは、競馬ドメインの語彙で説明できる判定・計算・変換をコマンドの中に直書きするから。本リポジトリはリッチドメインモデルを採る(docs/ARCHITECTURE.md レイヤー構成)。
- その if / 計算 / 変換をドメインの言葉で一文で言えるか(「直近5戦の着順から直近成績スコアを出す」「騎手のコース別勝率から騎手スコアを出す」)→ 言えるなら domain に置く。実例: スコア計算は
Horse/Jockey/Trainerエンティティが内包し、ScoringOrchestratorは取得・組み立て・委譲だけを行う薄い層 - commands に残してよいのは段取りだけ: リポジトリ/オーケストレータを呼ぶ・結果を保存する・表示する。コマンド本体は名前付きステップの平坦な列挙になる
- domain の型が フィールドしか持たず判定を持たないなら貧血症の兆候。そのデータへの判定・導出は近くのコマンドに散らばっているはずなので、該当エンティティ/値オブジェクトに引き上げる
- 定数・重みの唯一の定義箇所を作る。実例: 10要素の重みは
src/constants/ScoringConstants.tsのSCORE_WEIGHTSが唯一の正で、スコアリング(ScoreComponents.calculateTotalScore())も ML(MLFeatures extends ScoreComponentsData)も同じ定義を参照する。重みや閾値をコマンドやモデル側に再掲・再定義しない
判断に迷う場合の目安: そのロジックのユニットテストを書くとき DB(テスト用 SQLite)が1つも要らないなら domain 行き、リポジトリ呼び出しの順序や永続化が主題なら commands / services 行き。
原則 3: ポリモーフィズムの判断タイミング — 2箇所目の種別分岐を書く前
種別(馬場状態 良/稍重/重/不良、レースクラス、会場、距離カテゴリなど)による分岐を2箇所目に書こうとした瞬間が判断のタイミング。書いてからのリファクタリングではなく、書く前に問う:
- この軸は今後増えるか(会場・レースクラスのように増える)→ 種別ごとの実装 + レジストリにする
- レジストリは
Mapでなく **union の Record({ [K in Kind]: Impl })**で持ち、種別追加時の登録漏れをコンパイラに検出させる - 増えない・値域が閉じている(馬場4値のように仕様で固定)→ ポリモーフィズムは過剰。素直な if / exhaustive switch でよい
- 分岐が1箇所に閉じている exhaustive switch も許容。問題は同じ種別分岐が複数ファイル・複数関数に散った状態(散らばりの気配が出たら定数表・種別実装に寄せる)
- 分岐の閾値・係数は必ず
src/constants/に出す(マジックナンバーの散在はスコア再現性を壊す)
原則 4: トリッキーな実装の禁止 — 退屈で明示的に書く
「短い・巧い・一行で済む」は価値ではない。初見の読み手が10秒で意図を取れない書き方は、動いていても書き直す(驚き最小の原則)。
- 型ジャグリング(型を通すためだけのチェーン・条件付き型の曲芸・
asでの黙らせ)をしない。型が素直に付かないのは設計側の歪みのシグナル。anyは使わない(docs/DEVELOPMENT.mdコーディング規約) - 偶然の一致による再利用をしない(「たまたま同じ形」の2つの概念を1つの関数・型で兼用しない)
- インデックス演算・ビット演算・正規表現の技巧で「賢く」書かない。for-of + push、名前付き述語、素朴な分解で書く。HTML 抽出(
src/utils/HorseDataExtractor.ts)のように正規表現が本質的に必要な箇所は、意図をコメントで残す - 統計・ML の式(L2正則化ロジスティック回帰・標準化・softmax・較正)は式そのものが技巧に見えるので、何を計算しているかと出典・根拠をコメントで残す
第2部: 理解容易性の7観点(実装後セルフチェック)
実装を終えたタイミングで、識別子(変数・関数の名前)と区画(関数・クラスのまとまり)を以下の7問で確認する。
| # | 観点 | 問い | 本リポジトリでの具体ルール |
|---|---|---|---|
| 1 | 名称 | 分かりやすいか?(曖昧→明瞭) | 命名規約は docs/DEVELOPMENT.md(コマンド=動詞ベース PascalCase、エンティティ=名詞、リポジトリ=<対象><Query|Aggregate>Repository)。マジックナンバーは src/constants/ へ。名前と内容の一致(驚き最小) |
| 2 | 役割 | 複数ないか?(複数→単一) | 参照系は queries/、更新系は aggregates/ に分ける(両者を1クラスに混ぜない)。取得と計算と表示を1メソッドに混ぜない |
| 3 | 参照 | 広くないか?(広域→局所) | スコープは最小に。Database インスタンスは受け取って使う(グローバル接続を関数内で開き直さない)。重み・閾値は SCORE_WEIGHTS 等の単一定義を参照する |
| 4 | 状態 | 変えられなくできるか?(可変→不変) | readonly / as const を既定にする(SCORE_WEIGHTS は as const)。エンティティ・値オブジェクトは生成後に書き換えない |
| 5 | 面積 | 大きくないか?(広大→狭小) | 1ファイル 400 行・1関数 80 行・認知的複雑度 15 が lint の警告線(bun run lint)。既に超えているファイル(MachineLearningModel.ts / Horse.ts / Backtest.ts 等)を追記で太らせない。死にコード・コメントアウト旧コードは消す |
| 6 | 階層 | 深くないか?(多層→単層) | ガード節・早期 return を優先(if (!raceRecord) throw のように先に落とす)。ネストが深い処理は関数抽出。コマンド本体は平坦な列挙にする |
| 7 | 秩序 | 整っているか?(雑然→整然) | JSDoc で目的・引数・戻り値を書く(既存コードの @remarks / @example の書き方に揃える)。同じ意図の処理は対称に書く。層をまたがせない(commands から SQL を直接書かない) |
7観点の物差しはすべて「読み手の思考量を増やすか?」。迷ったら、その書き方が読み手に確認範囲の拡大(名称・参照・状態・面積)か複雑性への対処(役割・階層・秩序)を強いるかを考える。
クエリ層の約束(DB を触るとき)
- Kysely はリポジトリ層(
src/repositories)と DB 層(src/database)だけで使う。組み立てたクエリは.compile()してsrc/database/QueryRunner.tsのselectRows/selectRow/runStatementで実行する(ビルダーの.execute()系は DummyDriver のため空の結果が返る) - 実行は bun:sqlite の同期 API のまま。トランザクションは
DatabaseConnection.runInTransaction。非同期化してこの性質を壊さない
ドメイン固有の不変条件(スコアリング・ML を触るとき)
SCORE_WEIGHTSの合計は 1.0。要素の追加・重みの変更は合計を必ず再確認し、docs/ARCHITECTURE.md/docs/MODELS.md/ help スキルの配分表も追随させる- スコアリングと ML は同じ10要素を使う(特徴量の統一)。この 10 要素は
SCORE_WEIGHTS/ScoreComponentsData/FeatureBuilderのFEATURE_SPECS/MachineLearningModelの複数の手書きリスト /CalculateScore/Backtestに独立して並んでいるので、片方だけに要素を足さない(全箇所は review-scoring 3.2) - バックテスト・訓練データは未来情報を使わない。取得メソッドには
beforeDate/asOfを通し、集計テーブル(horse_course_stats/horse_track_stats)ではなく as-of 集計版を使う。beforeDateは省略可能な引数なので、渡し忘れても型エラーにならない点に注意する - ML の学習・予測は決定論的(L2正則化ロジスティック回帰。本番コードに乱数は無い)。同じ入力なら同じ結果が出る性質を壊す変更をしない。乱数を導入するならシードを固定し、テストから再現できる形にする
これらの機械的検出は review-scoring の守備範囲。本スキルは「書く前に壊さない」ための注意書き。
実装前チェック(4問)
- これから作るクラス・関数の引数・依存は5個未満か。超えるなら何を束ね・何を分けるか決めたか
- 書こうとしている判定・計算は競馬ドメインの言葉で言えるか。言えるなら domain(エンティティ/値オブジェクト)に置く場所を決めたか
- 種別での分岐を書こうとしていないか。この軸は増えるか。閾値・係数を
src/constants/に出したか - いま思いついた書き方は「巧い」から選んでいないか。より退屈な書き方があるならそちらを選んだか
責務境界
- レビュー時の機械的検出: 層・配置は review-arch、制御フロー・スタイルは review-code、命名は review-naming、テストは review-test、スコアリング/ML のドメイン正しさは review-scoring。層越え import・純粋性・接続生成箇所・
.skip/.only・eval系は Biome が error で落とす(一覧は review-code 2.1) - コードスタイルの正は review-code と
docs/DEVELOPMENT.md。本スキルは「書く前・書いた直後の判断」に限定し、チェックリスト・判定基準(OK/WARN/FAIL)を持たない
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
抽出済みJSONファイル(馬・血統・レース出走データ)をSQLiteデータベースにインポートする。「DBインポート」「データベースに保存」「JSONをDBに登録」といった依頼で発動する。
agent 向けテキスト指示(skill / slash command / task プロンプト / コード生成プロンプト)を、バイアスを排した実行者に動かしてもらい、両面(実行者の自己申告 + 指示側メトリクス)で評価して反復改善する手法。改善が頭打ちになるまで回す。Use when user says 'プロンプトを改善して', 'スキルをチューニング', 'empirical-prompt-tuning', or after creating/heavily revising a skill or prompt.
JRA公式サイトから特定レースの出馬表HTMLを取得して馬データを抽出する。「データ取得」「JRAからデータを取ってきて」「出馬表を取得して」といった依頼で発動する。
有馬記念分析システムで利用可能なスキル一覧と使い方、スコア配分、基本ワークフローを表示する。「ヘルプ」「使い方を教えて」「何ができるの」といった依頼で発動する。
登録済みの競走馬を血統情報(父・母・母父)、調教師、馬主付きで一覧表示する。「馬一覧」「登録されている馬を見せて」といった依頼で発動する。