Skip to content

言ったことを残す ​

AI に「受注照会の画面を作って。探すのは受注番号と顧客名で、承認ボタンを押したら選んだ行をまとめて確定にする」と頼めば、定義は出てくる。出てきた定義は動く。動くから気づけないのが問題で、あとから3つ困る。

困ることいま何が起きているか
言ったのに入っていない頼んだ絞り込みが1つ落ちている。画面は出るので、使う日まで分からない
言っていないのに入っている頼んでいない列やボタンが増えている。AI に書かせると必ず出る
未定のはずが決まっている「却下の理由は未定」と言ったのに、選択肢が3つ書かれている

設計書をコードから生成しても、これは消えない ​

hatake は定義から人の言葉を出せる(explain)。これは生成物なので古くならない設計書で、値打ちはある。それでも上の3つは消えない ── 言ったことが定義に書いていないから。意図・理由・決めた人・決まっていないことは、どこにも残っていない。

だから残すのは1枚だけ。言ったことを機械可読な形で置く。

意図の1枚 ​

定義の隣に <画面id>.intent.yaml を置く。

yaml
intent_version: "1.0"
page: order_search

asked:                                  # 人が言ったこと(原文のまま)
  - id: R1
    text: 受注を受注番号・顧客名・受注日の範囲で探せる
    covers: [filter:orderNo, filter:customer, filter:orderDate]
    by: 田中
    at: "2026-09-08"
  - id: R2
    text: 承認ボタンを押したら approveOrders を呼んで、選んだ行をまとめて確定にする
    covers: [action:approve]

decisions:                              # 業務の決めごと(理由つき)
  - id: D1
    text: 出荷済の受注は直せない
    why: 倉庫の締めが先に走るため
    covers: [field:status]

undecided:                              # 決まっていないこと
  - id: U1
    text: 却下の理由の選択肢は未定
    covers: [field:rejectReason]

acceptance:                             # 終わりの判定
  - validate --warn-as-error
  - run --scenario order.scenario.json

text は要約しない。 人が言ったまま置く ── 要約した時点で読んだ側の解釈が混ざり、それが言った言ってないの正体だから。

covers が肝。 「その要求が定義のどこに落ちたか」を指す。ここがあるから機械が突き合わせられる。

「決まっていない」を空欄と区別して持つ。 空欄は「まだ聞いていない」と「聞いたけど未定」を区別できない。undecided に書いてあれば、あとで誰かが決めたときに機械が気づける。

突き合わせる ​

bash
npx -p @hatake-fw/api hatake trace order_search.yaml
order_search: 要求 6 件 / 定義の相手 12 件
言ったのに入っていない(1 件):
  ・R3「却下の理由を選べる」が指している field:rejectReason は定義にありません=言ったのに入っていない、か、名前が違います。
未定なのに決まっている(1 件):
  ・U1「却下の理由の選択肢は未定」は未定のはずですが、定義には field:rejectReason が在ります=誰かが決めています。
由来の無い定義(2 件):
  ・column:memo は、どの要求からも来ていません。
  ・action:export は、どの要求からも来ていません。
終わりの判定:
  ・validate --warn-as-error
  ・run --scenario order.scenario.json

一番効くのは由来の無い定義。頼んでいない列やボタンが増えているのは、レビューで一番見つけにくい(動くので)。

言うこと落とすか
言ったのに入っていない(指す相手が定義に無い)落とす(確かに食い違っている)
未定なのに決まっている落とす
由来の無い定義言うだけ
どこに落ちたか書いていない要求・人が見ていない下書き言うだけ

落とすのを2つに絞ったのは、途中から始められるようにするため。 既にある定義に意図を後から書き始めると、最初は全部が「由来の無い定義」になる。そこで落とす道具は、1日で使われなくなる。全部を落としたいときは --require-intent(CI に置くときはこちら)。

最初の1枚は、指示文から起こせる ​

意図の器は在っても、書き始めるのが手間では続かない。指示文はもう在るので、そこは機械にやらせる。

bash
npx -p @hatake-fw/api hatake intent --draft --from 指示.md --definition order_search.yaml \
  --out order_search.intent.yaml

1行が1件になる。やることはそれだけ ── まとめない・言い換えない・順番を変えない。

機械が決めること決めないこと
行を件に開く(箇条書きは1行1件、散文は句点で分ける)何を言ったのか(text はその行のまま)
分類(見出しと合図の言葉だけで決める)見出しが無ければ全部 asked(推し量らない)
covers(業務の言葉が一致した所だけ)一致しなければ空。人が書く

AI への頼み方のテンプレで書いてあると、そのまま分かれる。

## 画面でやること
- 探す: 受注番号・顧客名        →  covers: [filter:orderNo, filter:customer]
- 見る: 受注番号・顧客名・金額  →  covers: [column:orderNo, column:customer, column:amount]

## 決まっていないこと
- 却下の理由の選択肢は未定      →  undecided へ

「受注番号」は絞り込みにも列にも在る。探す: / 見る: の書き出しで種類が決まるので、1件が両方を指さずに済む(推し量りではなく、テンプレが決めている字を読んでいる)。

起こしたものは全部 source: ai-draft。

yaml
  - id: R2
    text: "押す: 承認(選んだ行をまとめて)"
    covers: [action:approve]
    source: ai-draft

AI がこう読んだという下書きで、人が見て confirmed: true にするまで未確認のまま。trace はそれを「人が見ていない下書き」として挙げる ── 読まずに使うと、AI の解釈が人の言葉として固まってしまうので。

一緒に「人がやること」も返る。

・5 件を起こしました。**どれも AI が読んだ下書き**です(人が読んで confirmed: true にしてください)。
・R1「受注を探せる画面を作って。」は、定義のどこに落ちるか分かりませんでした(covers が空)。
・誰が言ったか(by)といつ言ったか(at)は指示文からは分かりません。
・action:detail は、どの要求とも言葉が一致しませんでした(頼まれていないか、言い方が違うだけです)。

要求を定義から生成してはいけない。 入力は指示文だけ ── 定義から起こせば必ず一致するので、突き合わせが何も言わなくなる。ここは規則として決めてある。

MCP を繋いでいれば hatake_intent がそのまま渡る(instruction に指示文、source に定義)。定義を書く前に呼び、書いたあともう一度呼ぶのが使い方 ── 2回目で covers が当たり、勝手に足したものが出る。

レビューに出すのは1枚 ​

突き合わせの結果は読めるようになったが、人に渡す紙にはなっていなかった。レビューに出すのは1枚がいいので、1枚に刷る。

bash
npx -p @hatake-fw/api hatake design order_search.yaml --intent order_search.intent.yaml \
  --out 設計書.md

並ぶのはこの順。

節どこから来るか
言ったこと(要求)意図の1枚(人が書く)+ trace の食い違い
読み返しexplain(定義から生成)
決まっていないことask(定義に書けない問い)+ 意図の undecided
書き足したほうがいい所advise
終わりの判定意図の acceptance
この紙が見ていないもの毎回同じ(サーバの中身・画面の見た目・業務として正しいか)

手で書く欄は1つも無い。 人が書くのは意図の1枚と案件の前書きだけで、この紙は刷り直す ── 手で書く欄を1つ作ると、そこだけが古くなって嘘をつく。刷った日付も入れない(同じ入力なら1バイトも変わらないので、差分がそのまま「何が変わったか」になる)。

渡していない紙の節は消さない。 --intent を渡さなければ「渡されていません」と書く。空の節にすると、読んだ人は「要求が無い案件」「未決は無い」と読むので。意図は画面1枚ごとの紙なので、app: の定義では --page <id> でどの画面かを決める(決めていなければ、決まっていないとそう書く)。

終了コードは動かない。合否は validate(事実)と trace(食い違い)の担当で、この紙はレビューに出すためのもの。

言わないこと ​

trace が言うのは対応の有無だけ。「意図どおりか」は言わない ── それは定義を読み返して(explain)人が決めること。機械が「意図どおりです」と言い出したら、その時点でこの仕組みは嘘をつく側に回る。

言った言ってないが完全に消えるわけでもない。text は人の言葉なので、解釈の幅は残る。消えるのは「入っていない/勝手に入っている」で、そこは機械が確実に言える。

頼むときの型と同じもの ​

AI への頼み方の「埋めるだけのテンプレ」は、そのままこの1枚の入力になる。テンプレの「やること・画面でやること・業務の決めごと・決まっていないこと・終わりの判定」が、asked / decisions / undecided / acceptance に順に落ちる。頼み方を決めておくと、設計書が副産物になる。