Skip to content

NDF Factory: 画面で行える操作を API と MCP からも同じく行える(機能から起こした配列の入出力・委任の認証・追記だけの操作の記録) #2384

Closed

Description

@takemi-ohama

親: #2168(Neural Development Factory を Web UI で操作できるサービスにする)。方針: マイルストーン「25 Neural Development Factory」のスプリント MVV の Value 2(画面先行)・Value 4(AI からも人と同じく使える)。

具体例

利用者が、自分の手元の Claude Code から「Factory で #2172 のスプリントを始めて、承認待ちになったら教えて」と頼む。今の Factory には MCP(Model Context Protocol)の受け口が無く、API は画面の都合で 1 件ずつ受け渡す形が混ざっている。この課題の後は、エージェントが利用者の委任で MCP からスプリントを始め、一覧を読み、承認の依頼を読める。ただし承認の確定は人だけが行う。だれの委任で、どのエージェントが、いつ何をしたかは、追記だけの記録に残る。

依頼(原文)

| AI からも人と同じく使える | 画面で行えるすべての操作を API と MCP からも行える。画面も同じ API を通る。委任を明示する認証・人と同じ認可・追記だけの操作の記録を持つ。承認ゲートとレッドラインの承認は人だけが行う |

| 画面先行 | UX の土台(画面の地図・部品・端末・オフラインの見せ方・国際化)→ 機能の画面をダミーデータで作り切る → API の中身をつなぐ、の順で作る。API の入出力は画面の 1 枚に合わせず機能から起こし、常に配列で受け渡す |

(書き手: 利用者、日付: 2026-11-02。#2168 の「方針(利用者の決定)」の表の 2 行。表は起票者が書き起こしたもので、利用者の決定として承認された行である)

画面で行えるすべての操作を API と MCP からも行えるようにする。画面も同じ API を通して動かし、画面にしか無い操作も、API や MCP にしか無い抜け道も作らない。ただし、承認ゲートの承認と、共通原則の C1〜C8・プロジェクト MVV の P に当たる操作の承認は人だけが行う。API と MCP からは承認の依頼と材料を読めるが、承認を確定できない(AI のエージェントの委任でも同じ)。API と MCP を使う者は、だれの委任で、どのエージェント・クライアントが動くかを確かめ、認可は人の操作と同じく掛ける。読み取りを含むすべての操作を、だれが・いつ・どの経路で・何を行ったかの追記だけの記録に残す。秘密と個人情報の値は記録に書かない。

(書き手: 利用者の承認を受けたスプリント MVV の Value 4、日付: 2026-11-02。文面は起票者が利用者の方針から起こし、利用者がスプリント MVV として承認した)

課題の題と、この後の「何をするか」の表・受け入れ条件の案(起票時の本文)は起票者が書いた。原文に無い条件は「起票者の推定」に分けた。

起票者の推定

題・本文のうち、利用者の指示の原文に無く起票者が推して足した条件。承認ゲート 1 で利用者が確かめるまで、利用者の決定として扱わない。

# 条件 推した出所 この要求での扱い
S1 配列の受け渡しで、一部だけ失敗したときの返し方・冪等性・配列の上限を決める 起票時の本文の「入出力の形」の行 受け入れ条件 6〜9、前提 3
S2 委任の権限は短い期限で、範囲を絞り、取り消せる 起票時の本文の「委任の認証」の行 受け入れ条件 11〜14、前提 4
S3 認可は要素ごとに掛ける 起票時の本文の「認可」の行 受け入れ条件 15
S4 操作の記録の項目は、操作した人・委任を受けたエージェントかクライアント・日時(UTC)・呼び出し元の種類(画面・API・MCP)・操作・対象の ID 起票時の本文の「操作の記録」の行。原文は「だれが・いつ・どの経路で・何を」 受け入れ条件 19
S5 画面の操作と契約の操作と MCP のツールの対応を、検査で確かめる 起票時の受け入れ条件の案 1 受け入れ条件 1〜3
S6 C1(秘密に触れる)・C2(認証・認可を変える)に当たる Factory の操作(モデルの認証情報の保存と削除、席の追加・変更・削除、チームの作成)は、委任の資格情報からは確定させず、人が画面で行う 起票者の推定(この要求で足した)。原文の「C1〜C8 に当たる操作の承認は人だけが行う」を、Factory には操作ごとの承認の仕組みが無いため「委任では確定させない」と読んだ 受け入れ条件 17、前提 5
S7 ランナー向けの経路(実行ジョブの経路・進捗の受け口)と GitHub の Webhook は、配列の規則と MCP の対象から外す 起票者の推定(この要求で足した)。画面で行える操作ではなく、GitHub の Webhook の形は Factory が決められないため 前提 2、含まない

目的

  • 人が任せた AI のエージェントや連携するシステムが、画面と同じ操作を、画面と同じ認可と記録のもとで行える状態にする
  • API の入出力を画面の 1 枚から切り離し、機能(エンティティと操作)単位の配列の形へそろえる。画面・API・MCP が同じユースケースを通る
  • 承認ゲートの承認と、人の承認が要る操作の確定を、委任の経路から行えないことを仕組みで保証する
  • だれの委任で、どのエージェントが、いつ何をしたかを後から確かめられる記録を残す

前提

  • 前提 1: 「画面で行える操作」は、画面の API クライアント(factory/web/src/api.ts の Api)が呼ぶ操作の全体と、承認と通知(NDF Factory: 承認ゲートの通知(Slack を含む)と Web からの承認 #2173)が足す Web からの承認の操作を指す。2026-11-04 の時点で Api は 31 の操作を持つ(request( の呼び出し 31 件)
  • 前提 2: 配列の規則と MCP のツールの対象は、利用者(人か、人の委任を受けたエージェント)が呼ぶ操作である。ランナー向けの経路(/run-jobs/*・/ingest/events・/ingest/api/events)と GitHub の Webhook(/ingest/github)は対象に含めない(S7)
  • 前提 3: 1 回の要求で受け渡す配列の要素は 100 個までとする。値は設計で実測を見て変えてよいが、上限を超えた要求は処理せずに 400 と上限値を返す、という振る舞いは変えない
  • 前提 4: 委任の資格情報の期限は 1 時間以内とする。範囲は「読み取りだけ」と「読み書き」の少なくとも 2 つを選べる。値と範囲の分け方は設計で決める
  • 前提 5: GitHub App の導入とリポジトリへの認可は、人がブラウザで GitHub の画面を操作する流れである。MCP からは導入と認可の URL を返すところまでを行い、GitHub の画面の操作は人が行う
  • 前提 6: 委任の資格情報の発行と取り消しは、人が画面で行う。委任の資格情報で委任の資格情報を発行できない
  • 前提 7: 既存の契約(factory/contracts/openapi/ の 5 ファイル)の経路を配列の形へ変える。画面と API は同じリポジトリで同時に変わり、外部の利用者はまだいないため、旧い形との互換の経路は持たない
  • 前提 8: 操作の記録を置く表の定義は、インフラ(private リポジトリ)が作る。このリポジトリは保存の契約(interface)と、メモリの実装・本物の実装を持つ

対象範囲

含む:

  • 契約(factory/contracts/openapi/)の利用者向けの操作を、機能(エンティティと操作)単位の配列の入出力へ起こし直すこと
  • 画面の API クライアントと、ダミーの API クライアントを新しい契約の形へ合わせること
  • MCP のサーバー(API と同じユースケースを呼ぶ)と、契約の操作と MCP のツールの対応表
  • 委任の資格情報の発行・検証・取り消しと、それを発行・取り消す画面
  • 人だけが確定できる操作の印と、その印のある操作を委任の資格情報から断る共通の処理
  • 操作の記録(追記だけ)の保存の契約・実装と、すべての利用者向けの操作からの書き込み
  • 画面の操作・契約・MCP の対応と、単一のレコードの受け渡しが無いことを確かめる検査

含まない:

  • ランナー向けの経路と GitHub の Webhook の形(前提 2)
  • Web からの承認の操作そのもの(NDF Factory: 承認ゲートの通知(Slack を含む)と Web からの承認 #2173 が作る)。この課題は、その操作に人だけが確定できる印が付き、委任の資格情報から断られる仕組みを用意する
  • 操作の記録を読む画面と、記録の検索の機能
  • 操作の記録の保存期間と、国ごとの法令(GDPR など)に応じた削除の手続き(未決 2)
  • 外部の利用者向けの公開の手続き(MCP のサーバーの一覧への掲載など)
  • インフラの定義(表・Lambda・API Gateway)

ドメインイベント

# イベント 引き金 失敗したとき 順序の前提
E1 人が委任の資格情報を発行した 人が画面で、委任するクライアントの名前・範囲・期限を選んで発行する 範囲か期限が許す値の外なら 400 で、発行しない 人がログインしている
E2 エージェントが委任の資格情報で API か MCP を呼んだ エージェントが操作を送る 資格情報が無効・期限切れ・取り消し済みなら 401 で、操作を行わない E1
E3 委任の資格情報の範囲と、操作の要る範囲を照らした E2 範囲の外なら 403 で、操作を行わない E2
E4 人と同じ認可を要素ごとに掛けた E3 認可の無い要素は、その要素だけを 403 相当の結果にする E3
E5 人だけが確定できる操作を断った E3 で、操作に人だけが確定できる印がある — (断ること自体が結果) E3
E6 配列の要素ごとに操作を行い、要素ごとの結果を返した E4 一部の要素だけが失敗したら、成功した要素はそのまま残し、失敗した要素に理由を付けて返す E4
E7 同じ冪等の鍵の作成を 2 回受け、2 回目は 1 回目の結果を返した E6 で、作成の要素に既に使われた冪等の鍵がある — E6 の 1 回目
E8 操作の記録を 1 件追記した E2〜E6 のすべての結果(成功・一部失敗・401・403・400) 追記に失敗したら操作を失敗として返す(前提として未決 1) E2
E9 人が委任の資格情報を取り消した 人が画面で取り消す 既に取り消し済みなら何も変えずに成功を返す E1
E10 取り消した委任の資格情報での要求を断った E9 の後の E2 — E9

E8 の「追記に失敗したとき操作も失敗にするか」は、記録の欠けを許すかと可用性の取り合いである。未決 1 に置いた。

用語

用語 意味
委任の資格情報 人が画面で発行し、エージェントかクライアントに渡す、期限と範囲つきの資格情報。だれの委任か(利用者)と、どのクライアントか(人が付けた名前と識別子)を持つ。人が取り消せる
人だけが確定できる操作 承認ゲートの承認・判断の依頼への答えと、共通原則の C1〜C8・プロジェクト MVV の P に当たる Factory の操作。契約に印を持ち、委任の資格情報からは断る
操作の記録 利用者向けの操作 1 回ごとに 1 件追記する記録。書き換えと削除の経路を持たない
呼び出し元の種類 操作の記録に残す、操作がどこから来たかの区分。画面・API・MCP の 3 つ
機能から起こした契約 経路と入出力を、画面の 1 枚でなくエンティティと操作から決めた契約。取得・作成・更新・削除とも配列で受け渡す

受け入れ条件

対応と形:

  • 1. 画面の API クライアントが呼ぶ操作がすべて契約(factory/contracts/openapi/)の operationId に対応することを、検査(npm run check の一部)が確かめる。対応しない操作が 1 つでもあると検査が落ちる
  • 2. 契約の利用者向けの操作がすべて MCP のツールに対応し、MCP のツールがすべて契約の操作に対応することを、検査が確かめる。片方にしか無い操作かツールが 1 つでもあると検査が落ちる
  • 3. MCP のツールは、API の経路と同じユースケースの関数を呼ぶ。MCP のツールから、契約に無い保存の操作を呼べないことをテストで確かめる
  • 4. 契約の利用者向けの操作で、要求の本文か応答の本文が配列でない操作が無いことを、検査が確かめる(1 件の取得・作成・更新・削除も要素 1 個の配列で受け渡す)
  • 5. 経路の変数で単一のレコードを指す取得(/sprint-runs/{sprintRunId} の形)が、利用者向けの操作の契約に無いことを、検査が確かめる

配列の受け渡し:

  • 6. 101 個の要素を送ると、どの要素も処理されずに 400 が返り、応答に上限の値 100 が入る
  • 7. 3 個の要素のうち 1 個だけに認可が無いとき、応答は入力と同じ順に 3 個の結果を返し、認可の無い 1 個だけに理由(forbidden)が付き、残り 2 個は処理される
  • 8. 0 個の要素を送ると、何も処理されずに 400 が返る
  • 9. 作成の要素に同じ冪等の鍵を付けて 2 回送ると、レコードは 1 件だけでき、2 回目の応答は 1 回目と同じ識別子を返す

委任の認証:

  • 10. 人のログインの token で呼んだ操作と、同じ人が発行した委任の資格情報で呼んだ同じ操作は、同じ認可の判定になる(人に許された操作は委任でも許され、人に断られる操作は委任でも断られる)
  • 11. 期限を過ぎた委任の資格情報で呼ぶと 401 になり、操作は行われない
  • 12. 取り消した委任の資格情報で、取り消しの後に呼んだ最初の要求が 401 になる
  • 13. 「読み取りだけ」の範囲の委任の資格情報で作成・更新・削除の操作を呼ぶと 403 になり、操作は行われない
  • 14. 委任の資格情報で委任の資格情報の発行を呼ぶと 403 になる(前提 6)
  • 15. 認可は要素ごとに掛かる。他人のアカウントの要素を混ぜた配列で、他人の要素だけが断られ、応答に他人のデータが含まれない

人だけが確定できる操作:

  • 16. 契約で、人だけが確定できる操作に印が付いている。ループの判断待ちへの答え(answerLoopPending)には印がある
  • 17. 印のある操作を委任の資格情報で呼ぶと、範囲が「読み書き」でも 403 になり、答えも状態も保存されない。印のある操作をすべて委任の資格情報で呼ぶテストがある(S6 の操作を含む)
  • 18. 委任の資格情報で、承認の依頼とその材料(スプリントの詳細・承認待ちの一覧)は読める

操作の記録:

  • 19. 利用者向けの操作 1 回ごとに、操作の記録が 1 件増える。記録は、操作した利用者の ID・委任の資格情報の識別子(人の直の操作では空)・日時(UTC)・呼び出し元の種類(画面・API・MCP)・operationId・対象の ID の配列・結果(成功・一部失敗・断った理由)を持つ
  • 20. 読み取りの操作と、401・403・400 で断った操作も記録に残る
  • 21. 操作の記録の保存の契約に、書き換えと削除の関数が無い。テストが契約の関数の一覧を確かめる
  • 22. モデルの認証情報(API キー・ロールの ARN)・ログインの token・委任の資格情報の値・GitHub の token を含む要求を送っても、記録にそれらの値が入らないことをテストで確かめる
  • 23. 記録に、メールアドレス・氏名・チームの名前・課題の題と本文が入らない

退行しないこと:

  • 24. 契約の形を変えた後も、画面の既存の画面テスト(factory/web の Vitest と Playwright)が通る
  • 25. ランナー向けの経路(/run-jobs/*・/ingest/*)の入出力が変わらない(前提 2)
  • 26. 委任の資格情報を使わない人の操作で、今と同じく 401(token の検証の失敗)と 403(アカウントの解決の失敗)が返る
    前提の確認: factory/api/src/auth/verify.test.ts と factory/api/src/handlers/api.test.ts が今の 401・403 を確かめている(実装の前に npm run check で通ることを確かめる)

非機能の条件

大項目 条件
性能・拡張性 配列の要素は 1 回の要求で 100 個まで。超えたら 400 と上限値を返す(前提 3)。画面は今と同じ回数以下の要求で同じ画面を描ける(今は詳細を要素の数だけ別々に取っている。factory/web/src/api.ts の sprintRunDetails・stepDetails・backlogs)
運用・保守性 1 つの操作を、操作の記録の 1 件と、その操作の要求の識別子でたどれる。秘密と個人情報の値は記録に書かない(受け入れ条件 22・23)
移行性 旧い形の経路との並走は持たない(前提 7)。画面と API は同じ Pull Request の系列で同時に切り替える
セキュリティ 委任の資格情報は期限 1 時間以内で、取り消しの後の最初の要求から断る。委任の資格情報の値は保存の時点でハッシュにし、平文で持たない。人だけが確定できる操作は委任から断る
システム環境 MCP のサーバーは factory/api/ と同じ Node 24 の上で動き、新しい実行基盤を足さない。足す場合は理由と代わりの案を設計に書く(スプリント Value 6)

影響

対象 影響
公開インタフェース 変わる。factory/contracts/openapi/ の利用者向けの経路の入出力が配列の形になる。互換なし(前提 7。Factory はまだ外部の利用者に公開していない)
データ 操作の記録の表と、委任の資格情報の表が増える(定義はインフラが作る)。既存の表の形は変わらない
既存の振る舞い 画面の API クライアントとダミーの API クライアントが新しい形を呼ぶ。利用者から見た画面の振る舞いは変わらない

検証手段

項目 手段
起動 cd factory && npm run dev:dummy -w web(ダミーの API クライアントで画面を起動する。手順は docs/specifications/ndf-factory-screen-map.md の「ダミーで起動する」)
テスト cd factory && npm run check(契約の検査・各 workspace の型検査と Vitest)と npm run e2e -w web(Playwright)
静的解析・型検査 npm run check に含まれる tsc --noEmit と OpenAPI の lint(Spectral)
手動確認 リリース後に、人が手元の Claude Code に Factory の MCP のサーバーを登録し、委任の資格情報でスプリントの一覧を読めることと、承認の依頼に答えようとすると断られることを見る

前提とする取り決め

項目 参照先 / 決めたこと
プロジェクト構造 変更は factory/ の中に置く。plugins/ と .claude-plugin/marketplace.json は factory/ を参照しない(python3 scripts/check-service-boundary.py)。インフラの定義と秘密は private リポジトリに置く(AGENTS.md の「マーケットプレイスの構造」)
コーディング規約 factory/ の TypeScript の規約と npm run check(docs/specifications/ndf-factory-accounts-and-plans.md の「パッケージの管理」)。契約の正は OpenAPI で、型は生成する(docs/specifications/ndf-factory-contracts-and-progress.md)
テスト戦略 契約と対応の検査は factory/contracts/tests/、委任・認可・記録の振る舞いは factory/api/ の Hono の app.request() を使うテスト、画面は既存の Vitest と Playwright

境界

区分 内容
常に行う npm run check の実行、契約からの型の生成、変更する経路のテストの追加
確認してから行う 依存パッケージの追加(MCP の SDK など)、契約の経路の名前の変更、記録の項目の追加。いずれも承認ゲート 1・2 で人が見る
行わない ランナー向けの経路の形の変更、インフラの定義の追加、plugins/ から factory/ への参照、操作の記録への秘密と個人情報の値の書き込み

未決

項目 誰が決めるか 期限
1. 操作の記録の追記に失敗したとき、操作も失敗にするか(記録の欠けを許さない側)、操作を通して後から補うか(可用性の側) 設計で案を出し、承認ゲート 1 で利用者が決める 設計 PR のマージ
2. 操作の記録の保存期間と、個人の削除の求め(GDPR など)への応じ方。記録が持つ利用者の ID を、削除の求めのときにどう扱うか 利用者 外部の利用者へ公開する前
3. 読み取りを含むすべての操作を記録する費用。画面の再読み込み(factory/web/src/poll.ts)の回数を含めた月の件数と保存の費用の見積り 設計で実測から見積り、承認ゲート 1 で利用者が見る 設計 PR のマージ
4. 規模(Size 13)が 1 回の承認で読み切れる量に収まるか。収まらないなら、契約の配列化・委任の認証と人だけが確定できる操作・操作の記録・MCP のサーバーの 4 つへ分けるか 設計で分け方の案を出し、承認ゲート 1 で利用者が決める 設計 PR のマージ
5. MCP の委任の認証を、MCP の認可の仕様(OAuth 2.1)に沿わせるか、Factory が発行する資格情報を直に渡す形にするか 設計 設計 PR のマージ

関連

進行

モード: — / 進め方: auto

  • 要求と受け入れ条件 — 2026-11-04 15:29
  • 作業場所の用意 — 2026-11-04 22:15
  • 設計 — 2026-11-04 15:34
  • 素材の収集と出典の確定
  • ドキュメント再構成 — 2026-11-04 15:57
  • ドキュメントレビュー
  • 計画 — 2026-11-04 22:15
  • 実装 — 2026-11-04 22:15
  • 構造改善 — 2026-11-05 00:25
  • 実装レビュー — 2026-11-05 00:50
  • 完了判定 — 2026-11-04 22:47
  • Pull Request — 2026-11-04 23:06
  • 確定仕様化 — 2026-11-05 03:28
  • 後片付け — 2026-11-05 03:53
  • 配布 — 2026-11-05 01:34
  • 体裁レビュー
  • リリース後テスト — 2026-11-05 03:48
  • 振り返り — 2026-11-05 03:48
  • 棚卸し — 2026-11-05 03:54

人の判断待ち(開発ループ lfactory3)

振り返り: #2532 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions