はじめに
社内の勤怠管理システムを、AWSのAgentic IDE Kiro で作っています。
Excelテンプレートへの出力、残業申請の承認フロー、5種類のロール権限、月次締め処理……と要件を詰めていったら、画面設計書だけで6画面分、共通ルールとDBスキーマを含めて数百行の設計書ができあがりました。
それを目の前にして、ふと不安になりました。
これ、一気にKiroに流したら混乱するのでは?
結論から言うと、この直感は正しかったです。そして「じゃあどう分けるのか」を考えた結果、Kiroの Steering という機能の本当の使いどころが見えてきました。
この記事では、以下を実例つきで書きます。
- 巨大な設計書を Steering と Spec にどう分解したか
- Steeringに何を書き、何を書かなかったか
- AIが誤解した箇所をSteeringに「書き戻す」運用サイクル
-
.kiroの場所を勘違いしていた話(やらかし)
対象読者は、KiroやCursor、Claude Codeなどでそこそこの規模のシステムを作ろうとしている人です。ツールは違っても、ルールファイル(.cursorrules、CLAUDE.md など)を持つツールなら考え方はそのまま応用できます。
環境
- Kiro(2025年7月プレビュー公開 → 2025年11月GA)
- 2026年7月現在、IDE / CLI / Web / モバイル(iOS) の4インターフェース
- 本記事はIDE版が前提
- 対象プロジェクト: Spring Boot 3.x + React(MUI) + PostgreSQL / Docker / VPS
セキュリティ注記: 2026年4月に細工されたプロジェクトファイルによる任意コード実行の脆弱性(CVE-2026-4295)が報告されています。v0.8.0で修正済みなので、古いバージョンを使っている人は先にアップデートしてください。外部から受け取ったプロジェクトを開く運用をしているなら特に。
1. なぜ一気に流してはいけないのか
起きること
6画面分の設計書+共通ルール+スキーマを1つのSpecに突っ込むと、こうなります。
① 序盤の制約を忘れる
タスクが数十個規模に膨らむと、コンテキストの前方にある指示ほど効きが薄れます。「テンプレートは新規生成せず既存ファイルに書き込む」という最重要ルールを冒頭に書いていても、後半のタスクではしれっと無視されます。これはAIの構造的な性質なので、書き方を工夫しても限界があります。
② 依存関係を無視した順序で作り始める
計算ロジックが存在しないのに、それを表示する画面から作り始めたりします。人間なら「まず計算からだろう」と考えますが、設計書に並んでいる順に着手されると、後で全部やり直しになります。
③ 失敗の切り分けができない
巨大な1回の生成が微妙な出来だったとき、どの指示が悪かったのか特定できません。全部投げ直すことになります。
分解の軸は「寿命」
じゃあどう分けるか。ファイル数や画面数で機械的に割るのではなく、その指示の寿命で分けるのが正解でした。
| Spec | Steering | |
|---|---|---|
| 役割 | 今回作る機能の指示書 | 常に守るルール集 |
| 効く範囲 | そのSpecの作業中だけ | 全Spec・全チャット・全生成に常時 |
| 寿命 | 実装完了で役目を終える | プロジェクトが続く限り有効 |
| 例 | 「勤怠計算サービスを作れ」 | 「秘密情報を直書きするな」 |
Steeringは .kiro/steering/ に置いたMarkdownで、そのプロジェクトの全AI対話に常時注入される永続システムプロンプトとして機能します。
図にするとこうです。
例えるなら、Specが作業員に渡す作業指示書で、Steeringは壁に貼ってある安全規則です。作業指示は現場ごとに変わりますが、安全規則は毎回貼り直しません。
2. 実際の分解
数百行の設計書を、こう振り分けました。
Steering: 4本に分けた
.kiro/steering/
├── template-output.md … Excelテンプレート出力の鉄則
├── security.md … 秘密情報の扱い・SSH・配信設定
├── domain-rules.md … 業務ルール(権限・残業計算・締め処理)
└── ui-standards.md … UI標準(色・書式・確認ダイアログ)
分け方の基準は「将来これだけ差し替えることがあるか」です。UIフレームワークを変えるなら ui-standards.md だけ書き直す、業務ルールが変わるなら domain-rules.md だけ、という単位にしておくと保守が楽になります。
中身の雰囲気はこんな感じです(domain-rules.md から抜粋)。
## ロールと権限(5ロール固定)
- Role: ADMIN / SOMU / DEPT_MANAGER / LEADER / MEMBER
- 参照系APIは必ず visibleEmployeeIds を経由する
(画面側の出し分けだけで制御しない)
- スコープ外のID直接指定は404を返す(403ではなく。存在自体を隠す)
- ロールの追加・変更はこのsteeringの更新を伴う(コードだけで増やさない)
最後の行がポイントです。「ルールを変えたければここを直せ」と書いておくと、AIが勝手にロールを増やす方向に流れにくくなります。
Spec: 依存順に6本
① ドメイン基盤 … スキーマ + 計算ロジック(画面なし・テストまで)
② 勤務表入力画面 … ①に依存
③ 残業申請+承認 … ①②に依存
④ 外部連携アダプタ … ③に依存
⑤ 締め+帳票出力 … ①〜③に依存
⑥ 一覧・管理画面 … 全部に依存
①を最初に完成させるのが肝でした。計算ロジックは全画面が依存する心臓部なので、ここだけは画面なしで単体テストまで固めてから次に進みます。逆にここが曖昧なまま画面を作らせると、Kiroが画面ごとに微妙に違う計算を勝手に実装するという最悪のパターンに入ります。
二重に書かない
分解で一番気をつけたのは同じルールを両方に書かないことです。
Specには「詳細ルールはsteeringを参照」とだけ書きます。両方に書くと、片方だけ直したときに食い違って、AIがどちらに従うか揺れます。真実の源はひとつ、が鉄則です。
3. 流し方:tasks.mdで一度止める
Kiroの仕様駆動開発は requirements.md → design.md → tasks.md → 実装 と進みます。ここでtasks.mdの生成後に必ず人間が確認するのを挟みます。
最初のプロンプトはこれだけです。
.kiro/specs/domain-foundation/ の実装を開始してください。
既存の実装は参照せず、steeringとこのspecのみに従ってください。
まずtasks.mdを生成して見せてください。
チェックするのは3点。
- スコープ外(このSpecでは画面)を作ろうとしていないか
- 依存順(スキーマ → 計算 → テスト)になっているか
- 要件に書いた具体的なテストケースがタスクに入っているか
3番目について補足すると、requirements.mdに期待値つきの実例を1つ書いておくと精度が跳ね上がります。
テストには以下の実例ケースを必ず含める SHALL:
- 中抜けケース: 9:00出勤・21:30退勤・休憩12:00-13:00・中抜け17:30-19:30、
所定17:30退勤(所定実働7.5h)
→ 実働9.5h / 申請残業2.0h / 換算退勤19:30 / 給与残業1.5h
計算ロジックをAIに書かせるとき、正解の数値が1組あるだけで結果が安定します。
4. 本題:AIが誤解した箇所こそSteeringに書く
ここからが実際にやってみて一番の学びでした。
事例1:名前が仕様を誤解させる
Specに「月40h相当超過(シフト勤務)」という項目を書いていました。すると実装中にKiroから質問が来ます。
「月の実働合計が月40h相当を超えた分」として設計しました。実務上の意図は「所定実働合計 + 40h」でしょうか?
どちらも違います。正解はこうでした。
月法定基準超過 = max( 月実働合計 − 月の日次残業合計 − 所定労働日数 × 8h, 0 )
式に 40という数字は出てきません。「40h相当」は「週40h ≒ 1日8h×5日」という換算の名残で、月版では「所定労働日数×8h」がその基準になります。名前だけが独り歩きしていたわけです。
さらにKiroの解釈には日次残業の控除が抜けていました。これを省くと、すでに日次残業としてカウント済みの時間を二重計上します。
ここで大事なのは、Specだけ直して終わりにしないことです。名前が誤解を生んだのだから、名前と式の両方をSteeringに書き戻します。
- 週40h超過(通常) / 月法定基準超過(シフト)は別枠として集計し、合算しない
- 週40h超過 = Σ各週 max(週実働合計 − 週の日次残業合計 − 40h, 0)
- 月法定基準超過 = max(月実働合計 − 月の日次残業合計 − 所定労働日数 × 8h, 0)
- いずれも法定8h基準で計算する(所定実働7.5hではない)
- 日次残業の控除は必須(二重計上防止)
名前だけ変えても意味がないのがポイントです。式が書かれていなければ、次に読んだAIがまた推測します。
事例2:粒度の勘違い
もうひとつ。締め処理の編集可否判定で、Kiroからこんな確認が来ました。
手動締めは日時で設定されるので、時刻粒度の締め切りを持つ形であってますか?
これも違いました。手動締めは「その時刻までは編集可」という締め切り時刻ではなく、「締めた」という状態の記録です。総務が締めボタンを押した瞬間から編集不可でなければ困ります。
public boolean isEditable(LocalDate targetDate) {
var closing = repo.find(YearMonth.from(targetDate));
// 手動締め済み → 存在するだけで編集不可(時刻比較しない)
if (closing != null && closing.manuallyClosedAt() != null) return false;
// 未締め → 自動締め日(翌月第2営業日)の終端まで編集可
return !LocalDate.now().isAfter(resolveAutoClosingDate(...));
}
これもSteeringに書き戻しました。
- 手動締め(manually_closed_at)は「締め切り時刻」ではなく「締められた記録」。
値が存在する時点で時刻比較せず編集不可とする
(resolveClosingDate の戻り値との日付比較だけで判定しない)
運用サイクルとして
この2件から見えたのは、こういうサイクルです。
★の一手間が効きます。 Specだけ直すと、次に別のSpecを作るときに古い理解から誤りが再生産されます。Steeringは全Specに効くので、ここに書けば一度で終わります。
余談ですが、この2件の質問はどちらもKiroが自発的に確認してきたものです。Steeringを読んで整合性をチェックできている証拠でもあるので、ルールファイルが機能しているサインとして受け取っています。
5. やらかし:.kiro の場所を勘違いしていた
正直に書きます。
私はしばらく、.kiro の下にプロジェクトを置くものだと思っていました。
~/Documents/Kiro/ ← ここがKiroのホームだと思っていた
├── .kiro/
├── 勤怠システムのソース
├── counter-app/ ← 別の練習用アプリまで同居
└── ...
正しくは逆で、各プロジェクトが .kiro を1つずつ持ちます。.git や .vscode と同じです。プロジェクトフォルダをKiroで開くと、その配下に .kiro/ が自動生成されます。
my-project/
├── .kiro/
│ ├── steering/ ← プロジェクトルール
│ ├── specs/ ← 仕様書
│ ├── hooks/ ← 自動化トリガー
│ └── skills/ ← カスタムコマンド
├── src/
└── README.md
なぜ勘違いしたか(半分は正しかった)
言い訳させてください。~/.aws や ~/.ssh のように、ドット始まりのフォルダはホーム直下の共通設定という先入観がありました。
そして調べたら、実際にグローバルな ~/.kiro/ は存在します。
~/.kiro/skills/ ← どのプロジェクトでも使えるカスタムコマンド
Skillsにはグローバル領域があるんです。だから「.kiro はホームにあるもの」という感覚自体は半分正しくて、Steeringとspecsはプロジェクト単位というのが正解でした。
この勘違いの実害
放置していたらどうなったか。
複数のシステムが1つのリポジトリに同居すると、Steeringが混ざります。勤怠システムの「テンプレートは不変のシェルとして扱う」「残業は二基準で管理」みたいな固有ルールが、まったく関係ない別アプリの開発に注入されます。無関係なコードがコンテキストに入るほど、精度も落ちます。
正しくはこうです。
~/Documents/projects/
├── attendance-system/ ← .kiro/steering は勤怠のルール
│ └── .kiro/
└── backend-lecture/ ← 完全に独立
└── .kiro/
ただしSecurityだけは使い回す
とはいえ、4本すべてがプロジェクト固有かというとそうでもありません。
-
持ち出せない:
template-output.md(あのExcel専用)、domain-rules.md(勤怠の業務ルール) -
持ち出せる:
security.mdの大部分
秘密情報を直書きしない、環境変数から読む、.env はgit管理外、AIのセルフチェックは一次フィルタで最終ゲートは人間——これらは次に何を作るときも同じです。
なので security.md は個人の汎用テンプレートとして手元に保管しておき、新しいプロジェクトを始めるたびにコピーして置く運用にしました。毎回考え直さなくて済みます。
6. 開発が終わってからが本番
最後にひとつ。「開発が完了したらSteeringは消すべきか」を考えたのですが、答えは残すでした。
Specは今回の実装限りなので、完了したらアーカイブでいいです。でもSteeringの本当の出番は完了後に来ます。
半年後にバグ修正でAIを使うとき、来年機能を追加するとき、モバイル版を作るとき——そのときSteeringがなければ、AIは何の制約も知らないままコードを書きます。テンプレートを作り直す実装に逆戻りしたり、権限フィルタを通さないAPIを足したりする危険が復活します。
ただし完了時に整理はします。
- 残す: 恒久的なルール(大部分)
- 削る: 移行期限定の記述。古いルールが残っている方が有害で、AIが不要なコードを書く原因になる
- 足す: 開発中にAIが誤解した箇所(前述のサイクル)
Steeringは書いたら終わりの設定ファイルではなく、プロジェクトと一緒に育てる資産です。.kiro/ は当然gitにコミットします。
まとめ
- 巨大な設計書を一気に流すと、序盤の制約を忘れ、依存順が崩れ、失敗を切り分けられなくなる
- 分解の軸は寿命。常に守るルール=Steering、今回作る機能=Spec
- Steeringは一括配置(順番なし)、Specは1本ずつ完了確認しながら
- 同じルールを両方に書かない(真実の源はひとつ)
- AIが誤解した箇所こそSteeringに書き戻す。Specだけ直すと誤りが再生産される
- 名前だけでなく式や定義まで書く。名前は独り歩きする
-
.kiroはプロジェクトごと。ただしSkillsにはグローバル領域がある - Steeringは開発完了後も残す。運用フェーズこそ本番
Kiroに限らず、ルールファイルを持つAIコーディングツールなら同じ考え方が使えるはずです。「AIが間違えたらルールに昇格させる」——このサイクルを回し始めてから、同じ指摘を二度しなくて済むようになりました。
