プロジェクト概要を先に作る
hatake は定義を書けば画面が出る。雛形を出して項目名を並べれば、その日には動くものが見える。いきなり書けるのがこの枠組みの売りだ。
けれど、いきなり書いていいのは何を作るかが決まっているときだけ。決まっていない状態で書き始めると、動く画面ができてから作り直すことになる。そして AI に書かせるほど、この失敗は速く大きくなる ── AI は決まっていないことを聞かずに、書ける方に倒すからだ。
| 決めていなかったこと | 出てくるもの | 気づくとき |
|---|---|---|
| このマスタは直せるのか | 編集ボタンと入力フォームが付いた画面 | 現場に見せたとき(「これ購買部のシステムで入れてます」) |
| 「取引先」と呼ぶのか「顧客」なのか | 画面ごとに customer と partner が混ざった定義 | 画面が10枚を超えたあたり |
| 項目名の付け方 | order_date と orderDate が同居した API | バックエンドと繋ぐとき |
| 誰が押していいボタンか | 誰でも押せる CSV 出力 | 監査で聞かれたとき |
どれも定義としては正しいので、検証も警告も何も言わない。書いていないことは検査に出てこない。だから先に決める。
先に決めるのは5つ
多くはない。1時間で埋まる。
| 決めること | 何を書くか | 決めないと |
|---|---|---|
| 何の業務か | 1〜3行。「卸売の受注。営業が電話で受けた注文を入れて、出荷指示まで出す」 | 画面の粒度が人によって変わる |
| 使う人と置かれ方 | 「営業10人・事務所の PC」「出荷担当・倉庫のタブレット・手袋のまま触る」 | ボタンの大きさも一覧の件数も決まらない |
| できないこと | 「商品マスタは購買部の別システムが正。こちらからは直せない」「締めたあとの受注は直せない」 | AI が書ける方に倒す(上の表の1行目) |
| データの出どころ | Repository の名前と、その向こうに誰が居るか(「商品マスタ=購買システムの REST・持ち主は購買部」) | 繋ぐ日まで「まだ無い」が見えない |
| 言葉 | 業務の言葉と、定義に書く名前の対(「取引先 → partnerCode」)。呼ばない言い換えも | 画面ごとに字が揺れる |
3番目の「できないこと」がいちばん抜ける。仕様書は普通「できること」を書く紙なので、書く欄が無いのだ。けれど画面を作る側にとっては、できないことのほうが強い制約になる。
読み物で終わらせない
ここまでを Word や Wiki に書いて終わりにすると、3か月後に誰も読んでいない。機械が読める1枚に落とす。定義の隣に hatake.project.yaml を置く。
project_version: "1.0"
system:
what: 卸売の受注。営業が電話で受けた注文を入れて、出荷指示まで出す。
users:
- 営業(10人・事務所の PC)
- 出荷担当(倉庫のタブレット。手袋のまま触る)
premises:
- 商品マスタは購買部の別システムが正。こちらからは直せない(照会だけ)。
- 締めは月末。締めたあとの受注は直せない。
external:
- name: productRepository
what: 商品マスタ(購買システムの REST)
owner: 購買部
glossary:
- term: 取引先
field: partnerCode
avoid: [顧客, 得意先]
note: 社内の帳票が全部「取引先」なので、画面もそう呼ぶ
naming:
page: snake_case
field: camelCase
role: snake_case
suffix:
date: Date読み返すのはこれ。
npx -p @hatake-fw/api hatake project置いておけば、道具は渡さなくても読む(定義の隣にあれば拾う)。
決めたことが、実際に守られる
概要を紙で持っているだけなら「読んでね」で終わる。1枚に落とすと、そのうち一部は機械が突き合わせられる。
npx -p @hatake-fw/api hatake advise page.yaml --project hatake.project.yaml# page.table.columns.1 [project-glossary-word]
こうなる: ラベル「顧客コード」に「顧客」が入っています。案件の決めごとでは「取引先」と呼びます(社内の帳票が全部「取引先」なので、画面もそう呼ぶ)。
書き足す: 「取引先」で言い直す。
# page.search.filters.0 [project-name-suffix]
こうなる: `type: date` の項目 "orderDay" は、案件の決めごとでは名前を "Date" で終わらせます。
書き足す: "Date" で終わる名前に直す(決めごと: naming.suffix.date = Date)。これは警告ではなく助言(終了コードは変えない)。名前と言葉は「書いたのに効かない」ではなく案件の好みなので、CI を落とす種類の話ではない。
雛形も決めごとの形で出る。
npx -p @hatake-fw/api hatake new crud --id order_entry --title 受注入力 --project hatake.project.yaml縛れているものと、縛れていないもの
ここは正直に分けて見ておいたほうがいい。読み返し(npx -p @hatake-fw/api hatake project)は毎回これを言う。
| 書いたもの | 誰が見るか |
|---|---|
用語(glossary)と名前の決めごと(naming) | 機械が定義と突き合わせる |
| 何の業務か・使う人・業務の前提・外の相手 | 人と AI が読むだけ。 誰も突き合わせていない |
つまり「商品マスタは直せない」と書いても、編集ボタンを足した定義は普通に通る。前提は定義に書きようがないので、機械には見えない。1枚に落とす値打ちは「守らせる」ことではなく、読まれる場所に置くことにある。
AI に渡す
MCP を繋いでいれば、最初に読む道具がこれになる(hatake_project)。順番は AI に書かせる にある。
繋がない使い方(Copilot・Cursor・ふつうのチャット)なら、AGENTS.md や CLAUDE.md に貼る断片を出せる。
npx -p @hatake-fw/api hatake project --agents # 貼る断片(印つき)
npx -p @hatake-fw/api hatake project --agents --merge AGENTS.md # 2回目以降:印の中だけを差し替える
npx -p @hatake-fw/api hatake project --agents --merge AGENTS.md --check # CI:貼った節が古くないか同じことを2か所に手で書くと、前書きを直したのに設定ファイルが古いが必ず起きる。だから1枚を正にして、貼る側は生成する。ブランチ名やコミット規約は前書きが持たない(定義に現れないものは機械が突き合わせられない)ので、そこは印の外に手で書いておけば、貼り直しても消えない。
貼ったあとに前書きだけ直すこともあるので、古くなったら CI が言う口も付けてある(--check。見るだけで書かない)。生成物なのに古い節が貼ってあると、AI はそれを読む。
どこまでを hatake がやるか、も先に見る
概要を書くと、hatake では書けないものが必ず混ざる。締め処理、承認フロー、認証、在庫の引当。これらは持たないと決めているので(この Framework が持たないもの)、誰がやるかを先に決めておく。
引ける表になっている。
npx -p @hatake-fw/api hatake where 締め処理 # → 枠組みの外(なぜ持たないか+画面側でできること)
npx -p @hatake-fw/api hatake where 承認 # → 枠組みの外(画面は状態を見せるだけ)
npx -p @hatake-fw/api hatake where 一覧の並べ替え # → 定義で書ける(書くキーと次に引く道具)
npx -p @hatake-fw/api hatake where --where outside # 持たないものの一覧区分は4つ。definition(定義で書ける)/plugin(アプリ側に登録して足す)/server(サーバの担当)/outside(枠組みの外)。
長い依頼文なら、まとめて仕分けられる(外が混ざっていたら先に言う)。
npx -p @hatake-fw/api hatake where --from 依頼.md仕分けた行は 7 件。**うち 3 件は枠組みの外**(当てられなかった行が 4 件)。
・締めたあとの受注は直せないようにする
[枠組みの外(hatake は持たない)] 業務ロジック(締め・引当・与信・按分)
・承認フローを組んで、承認されたら出荷指示に回す
[枠組みの外(hatake は持たない)] 承認フロー・状態遷移のエンジン当て方は言葉の一致だけなので下書き。当てられなかった行も必ず一覧に出る(黙って落とすと、仕分けたつもりで抜ける)。
outside と出たものは、前書きに置き場として記録しておく。premises に「できないこと」を書き、logic: に「何の規則が誰の担当か」を書く。
premises:
- 締めは会計システムが持つ。画面は「締めたら直せない」を見せるだけ。
logic:
- what: 締めたあとの受注は直せない
where: server
name: orderCloseGuard
why: 締めは会計側が持っているので、画面で判断すると二重管理になる書くのは名前と担当と理由だけ(規則の中身は書かない)。こうしておくと2つ効く。
- AI が最初に読む1枚に「これは外」が入る(
--agentsの断片にも載る) - 外の担当と書いたのに画面から呼んでいたら、道具が言う(
npx -p @hatake-fw/api hatake advise --project) - 言われた規則に担当が付いたかを突き合わせられる(意図の1枚から
covers: [logic:orderCloseGuard]で指す → 言ったことを残す)
AI に「締め処理も作って」と頼むと、外だと言えないまま Dart を書き始める。これはこの Framework でいちばんやってほしくないことなので、道具の側でも止めるようにしてある(MCP なら hatake_where)。
定義に書けないことは、道具が聞いてくる
仕分けても、聞かれていないから決めていないことが残る。排他(2人で同時に直したら)・ 採番(新しい番号は誰が付けるか)・論理削除(消したものは残るか)・端数(切り捨てか 四捨五入か)。どれも定義には書けないので、定義を読んでも空欄が無い=決まっていない ようには見えない。
画面を1枚書いたら、聞かせる。
npx -p @hatake-fw/api hatake ask order_entry.yaml返る問いには誰の担当か(サーバ/枠組みの外)と、この定義で聞いた理由 (どの画面の何を見たか)が付く。答えが決まったら、この前書きの logic に1行足して 印を付けると、次からその問いは出ない。
logic:
- what: 受注は更新日時が変わっていたら弾いて、読み直させる(後勝ちにしない)
where: server
answers: [concurrency]聞かれるのは12種類だけ。出なかった=決まっている、ではない(道具が毎回そう言う)。 → AI への頼み方
この1枚が育っているかを数える
前書きは足していく紙だ。1枚書いて満足して、画面が30枚になっても用語が3語のまま、 はよくある。育ち具合は数えられる。
npx -p @hatake-fw/api hatake project hatake.project.yaml --coverage definitions/決めごとの棚卸し(画面 2 枚・項目 14 個を読みました)。
用語: 3語(項目名を名指ししているのは 3語)
定義の項目 14 個のうち、辞書が名指ししているのは 3 個
・"nowhere" は辞書にありますが、定義のどこにも出てきません
名前の決めごと: 5/5 の対象に形を決めた
業務ロジックの置き場: 4件
問い返し: 答えた 1件・既定のままでよいと決めた 1件・まだ答えていない 1件総合点は付けない。 辞書が要らない案件もあるので、少ないことが悪いとは限らない。 定義を渡さなければ、そこは「数えていない」と言う(0 と出すと「全部載っている」に読める)。 この案件の説明と業務の前提は誰も突き合わせていないので、数にも出てこない。
育っているかは、1回の数では分からない
前回の棚卸しを渡すと、移り変わりが出る。
npx -p @hatake-fw/api hatake project hatake.project.yaml --coverage definitions/ --since 前回.json「画面だけ増えて用語が3語のまま」は事実として出る。ただし増えていない=悪い、とは 言わない(その領域が固まっただけかもしれない)。道具が言えるのは数の増減までだ。
最初の1枚は、資料から起こせる
白紙から書くのが重いなら、もう在る資料(提案書・要件メモ)から起こせる。
npx -p @hatake-fw/api hatake project --draft --from 要件メモ.md --out hatake.project.yaml拾うのは見出しのある所だけで、書いてある字のまま。拾えなかった行は捨てずに出るし、 拾えなかった所は TODO_ のまま残る(機械が埋めると、読んだ人は人が書いたものだと思う)。
定義を渡したら落ちる。 page: や app: から前書きを起こすと、前書きが定義と必ず 一致して読む値打ちが消えるので、そこは機械で止めてある。
辞書は、書いてあるものから起こせる
途中から前書きを入れるなら、辞書を白紙から書かなくていい。揺れている所は数えられる。
npx -p @hatake-fw/api hatake project hatake.project.yaml --drift definitions/同じ項目名に、違う言葉が付いています:
"amount"
・金額(12か所)
・支給額(1か所)「同じ項目名に違う言葉」と「同じ言葉が違う項目名」の2つの向きを見る。辞書に載せた字は 次から出ない ── 決めたら静かになる。
どちらが正しいかは言わない(業務の言葉なので、決めるのは人)。辞書も作らないし、 前書きも書き換えない。
--draft を足すと、そのまま貼れる形(glossary: の塊)で出る。term には多い呼び方を 仮に置いてあるだけで、正しいという意味ではない ── そう出力にも書いてある。 --since に前回の結果を渡せば、増えた揺れだけが出る(新しい画面を足した回に決めるのが いちばん軽い)。
決めたなら、落としてもいい
名前と用語の助言は好みなので、既定では終了コードを変えない。案件が「命名の揺れは直す」と 決めたなら、CI で落とせる。
npx -p @hatake-fw/api hatake advise app.yaml --project hatake.project.yaml --project-as-error落ちるのは案件の決めごとだけ(組み込みの助言では落ちない)。旗を渡さなければ絶対に 落ちない ── 助言を勝手に落とす道具になった時点で、事実を言う警告まで読まれなくなるから。
宣言した担当が、本当に登録されているか
「これはアプリ側の担当」と書いて、画面から呼ぶ所も書いた。登録を忘れると、押しても 何も起きない画面になる(押すまで気づけない)。
npx hatake advise app.yaml --project hatake.project.yaml --registry hatake-registry.json登録済みの一覧を渡したときだけ言う(知らないのに「登録が無い」と言うのは嘘なので)。 一覧は npx hatake registry <アプリのソース> で実装から作れる。
画面ごとの話は、この次
案件の前書きは案件ぜんたいの話で、1回書けばあとは足していくだけ。画面1枚ごとに「何を言われたか」を残すのは別の紙になる。
| 紙 | 何が書いてあるか | いつ |
|---|---|---|
プロジェクト概要(hatake.project.yaml) | 何のシステムか・できないこと・用語・名前 | 案件の最初に1回 |
| 頼み方のテンプレ | この画面で何をするか・業務の決めごと・終わりの判定 | 画面を作るたび → AI への頼み方 |
意図の1枚(<画面id>.intent.yaml) | 言われたこと・決めた理由・決まっていないこと | 画面を作るたび → 言ったことを残す |
作る順番
- プロジェクト概要を決める(このページ。1時間)
hatake.project.yamlに落とす(npx -p @hatake-fw/api hatake projectで読み返す)- AI に渡す(MCP、または
--agentsでAGENTS.mdに貼る) - 画面ごとに頼む(頼み方のテンプレ)→ 言われたことを残す(意図の1枚)
- 定義を書く・書かせる(機能別の書き方)→ 検証して読み返す(AI に書かせる)
- 決めごとと突き合わせる(
npx -p @hatake-fw/api hatake advise --project)
よくある失敗
- 概要を散文だけで持つ。 読まれないし、機械も見られない。せめて用語と名前は表にする
- 用語を決めずに始める。 あとから揃えるのは、項目名を変える話になる(Repository・API・試験・他の画面の遷移まで直すことになるので、道具は自動で当てない)
- 前提を書かない。 「できないこと」の欄が無い仕様書をそのまま渡すと、AI は書ける方に倒す
- 命名を後で揃えるつもりでいる。 決めごとを1枚に書いておけば雛形から従うので、最初に書くほうが安い
書き方の詳しい所(キーの意味・機械が見る範囲・--agents の印)は 案件の前書き。