Hora Kit が手順を 1 つも持たない理由
「Hora Kit の設計」シリーズの 2 本目。Hora Kit 本体(hora-core)は resolver の書き方もテーブルの形も持たず、「何がいつ起きるか」と「次に進む前に何が真でなければならないか」だけを持ちます。手順と合否基準を 4 つのスキルパッケージに分けた理由と、それがどう技術スタック非依存につながるのかを書きます。
1. はじめに
この記事は、弊社(Open Reach Tech)が OSS にした AI 開発フレームワーク『Hora Kit』の設計を解説するシリーズの 2 本目です。メイン記事はこちらです。
本当の意味での自動開発を実現するためのAI開発フレームワーク『Hora Kit』をOSSにしました
用語の確認。 Hora Kit で打つコマンドは
/horaの 1 つだけです。/horaが状況に応じて/hora-spec(仕様書を書く)、/hora-setup(実装リポジトリを作る)、/hora-plan(版を確定し、機能一覧と契約を書く)、/hora-build(1 機能を 18 の関所に通す)、/hora-accept(検収する)を順に呼びます。/hora-hotfixだけは/horaが呼ばず、人が直接打ちます。docs はこのうち/hora-specを「決める側」、残りを「作る側」と呼んでいて、この記事でもその呼び方を使います。
Hora Kit の設計の中で、説明すると一番意外そうな顔をされるのがこの話です。Hora Kit 本体は、resolver の書き方も、テーブルの形も、レビューが何で落とすかも、一切持っていません。持っているのは「何がいつ起きるか」と「次に進む前に何が真でなければならないか」だけです。
「それでどうやってコードが書けるのか」と思われるかもしれません。この記事では、なぜそうしたのか、実際にどう動いているのか、そしてそれが技術スタック非依存にどうつながるのかを順に書きます。
2. きっかけは、規約の写しが古くなる問題
この設計の出発点は、ごく普通の問題です。
AI エージェントにコードを書かせるとき、コーディング規約を skill として渡します。たとえば「stub resolver は server/graphql/resolvers/<audience>/stub/ に置く」のような規則です。実装の手順を説明するとき、この規則を手順の側にも書きたくなります。そのほうが 1 か所で読めて親切に見えるからです。
ところが、規約を持っているパッケージが更新されてパスが変わると、パッケージ側の規約は新しくなるのに、手順の側に書いた写しはそのまま残ります。しかも写しは「自分が古くなった」とは言ってくれません。docs はこれを「書かれた日と寸分違わず権威ある文面のままで、それに従ったエージェントは自信をもって間違った場所に成果物を置く」と表現しています。
人間のチームでも似たことは起きます。Wiki の規約ページと、実際のコードベースの規約が違っていて、誰も Wiki を直さない、というあれです。ただ人間なら「Wiki が古いのかも」と疑えます。エージェントは渡された文面をそのまま信じるので、この問題は人間のときより深刻でした。
3. Kit が所有するもの、パッケージが所有するもの
そこで弊社は、Hora Kit の規則を 1 つ決めました。スキルパッケージが既に持っている手順・規約・合否基準を、Hora の skill 側には書かない。「何をする作業か」だけを書いて、委譲する、という規則です。

| 所有するもの | 例 | |
|---|---|---|
| Hora Kit | 何がいつ起きるか。次に進む前に何が真でなければならないか | 「関所 4 は、この機能が足す全操作にスキーマ準拠の stub が存在したら通過」 |
| スキルパッケージ | どうやるか。何をもって「ちゃんとできた」か | 「stub は stub/{queries,mutations}/ に置き、スキーマを写し、DB アクセスを持たず、実装 resolver とクラス名を共有する」 |
この 2 つの文は重なりません。重ならないかどうかを判定する基準もあって、Kit のある行が、パッケージと突き合わせて「食い違っている」と判定できてしまうなら、その行は Kit にあるべきではない、というものです。単純ですが、Kit 側の文書を削るときの基準として効きます。
4. 4 つのパッケージ、119 のスキル
手順と合否基準は、ドメインごとに 4 つのパッケージに分かれています。
| パッケージ | 接頭辞 | 適用先 | スキル数 |
|---|---|---|---|
hora-skills-ort-core | hoc- | backend / frontend の両方 | 39 |
hora-skills-ort-renchan | hor- | backend リポジトリ | 31 |
hora-skills-ort-furo | hof- | frontend リポジトリ | 46 |
hora-skills-ort-support | hos- | 両方と、その周辺(説明・文書化) | 3 |
npm install を実行すると、この 4 つと @openreachtech/hora の中身が、フラットな 1 つの .claude/skills/ に並びます。
node_modules/@openreachtech/hora/dist/skills/<skill>/ ─> .claude/skills/<skill>/
node_modules/@openreachtech/hora-skills-ort-core/dist/skills/<skill>/ ─> .claude/skills/<skill>/
node_modules/@openreachtech/hora-skills-ort-renchan/dist/skills/<skill>/ ─> .claude/skills/<skill>/
node_modules/@openreachtech/hora-skills-ort-furo/dist/skills/<skill>/ ─> .claude/skills/<skill>/
そのままコピー。改名も書き換えもしない
接頭辞を付けているのは、フラットに並んだ一覧を見た人が「どれがどのパッケージ由来か」を一目で判別できるようにするためです。
5. 名前ではなく description でマッチする
ここが、この設計の一番細かい部分です。少し込み入りますが、この仕組みが無いと上の分離は成り立たないので、書いておきます。
Hora Kit のファイルは、パッケージのスキル名を 1 つも書きません。「例として」でも書きません。理由は、スキルの名前はパッケージのもので、パッケージは自由に改名できるからです。改名されたスキルは、何とも食い違いません。ただ名前が一致しなくなって、関所がその規約なしで走り、実行は「合格」を報告します。これは関所が絶対に起こしてはならない種類の失敗です。
その代わりに、実行時にこう動きます。
1. 関所・ステージ・検収手順が「どんな種類の作業か」を述べる
2. メインセッションが .claude/skills/ 配下の装備済みスキルの description を読み、
その作業を覆うものを選ぶ
3. どれを選んだかを、その関所に対して .hora/ に記録する
4. 選んだ名前を、作業を実行するエージェントに digest 付きで渡す
手順 2 をエージェントではなくメインセッションがやる理由は、再現性です。エージェントが自分で選ぶと、再実行のたびに違うものを選び、どちらを使ったのか誰も言えなくなります。手順 3 で選んだものを記録しておくと、パッケージの改名が diff で見えるようになります。
手順 4 の digest は、この規則が唯一認める「写し」です。ただし由来したパッケージとバージョンを名乗っていて、そのバージョンが動いた瞬間に読まれなくなります。実装エージェントは疑問が出た時点で元のスキルを開きます。
覆うスキルが見つからなかったときは、代わりを推測せず、「無い」と言って続けます。推測した代替品で進めるより、隙間を報告して進めるほうがましだ、という判断です。
6. スタック非依存への道筋
「手順を持たない」を徹底すると、もう 1 つ副産物が出ます。Kit はスタックの名前も持たなくなりました。
boilerplate の名前、フレームワーク、ミドルウェア、既定値。こういったものは Kit のファイルには一切書かれていません。代わりに、プロジェクトの docs/stack/ にあるスタック・ハンドブックが持っています。docs の言い方を借りると「場所そのものが契約」で、Hora の skill はプロジェクトルートの docs/stack/README.md を探し、無ければ止まって質問します。スタックを推測することはありません。
ハンドブックが答えるのは 5 つです。
| セクション | 確定すること |
|---|---|
| どこから来るか | リポジトリ URL と、取得するバージョンの選び方 |
| 何を埋めるか | boilerplate が持ってくるプレースホルダーと、それぞれに入れる値 |
| 何を置くか | boilerplate は同梱していないがプロジェクトに必要なファイル |
| その行へコピーするスキル | 作られたリポジトリ自身の .claude/skills/ へコピーする装備スキル |
| 届いたら何を読むか | /hora-setup が実物の木を読むときのチェックリスト |

つまり、Hora Kit を別のスタックに乗せたいとき、変えるのは 2 つだけです。そのスタックの手順と合否基準を持つスキルパッケージを hoX- の接頭辞で新しく書くことと、そのスタックの docs/stack/ ハンドブックを書くことです。Kit の skill は 1 行も変わりません。順序と関所はスタックを知らないからです。
7. 正直な現状
ここまで読んで「じゃあ Rails でも Django でも使えるのか」と思った方には、まだです、と答えなければなりません。
今日の時点で用意されているスキルパッケージとハンドブックは、弊社のスタック(backend が renchan、frontend が furo-nuxt)の分だけです。設計上は「パッケージとハンドブックを書けば乗る」ようになっていますが、まだ誰も書いていません。
それでも、この分離があるから「乗せられる」と言い切れます。hora-core はフレームワークや DB の名前を 1 つも持たない、一般的な Web 開発向けの構成です。手順を Kit に焼き込んでいたら、スタックを変えるたびに Kit 自体を書き直すことになっていたはずです。
弊社では、この「各技術スタックごとのスキル群を定義する」活動を一緒に進めてくれる開発パートナーを募集しています。自分たちのスタックの規約とレビュー基準を skill に落とし込んで Hora Kit に乗せたい、という方がいれば、Issue で声をかけてください。
8. まとめ
Hora Kit を使わない方にも、この分け方は使えると弊社は思っています。「いつ・何を」を持つ文書と「どうやって」を持つ文書を分け、前者は後者の名前を書かず、マッチは実行時に description に対して行う、というやり方です。
Claude Code の skill を組織で運用し始めると、規約の写しが方々に散っていきます。Hora Kit で弊社が採った答えは、写しを禁じて委譲だけを許す、でした。同じ問題で困っている方の参考になれば幸いです。
原典はこちらです。 https://github.com/openreachtech/hora-boilerplate/blob/main/docs/skills.ja.mdhttps://github.com/openreachtech/hora-core/blob/main/kit/skills/hora/references/structure.md