AI Agent Workstationをコンポーネント単位に再編し、DeploymentとRunnerの責務を分離する

実行時契約の整理からRepository全体の再編へ

前回の記事では、Todo SchedulerのTask GitとRunner契約を中心に、AI Agent Workstationの実行時の整合性を改善した内容をまとめました。

主な変更は次のとおりです。

  • Task Planのrequired_outputsを構造化してRunnerから報告する
  • Background AIのPlanningとExecutionを明確に分ける
  • Task単位のGit worktreeと段階的なmergeを管理する
  • terminal requestに冪等性を持たせる
  • SchedulerとRunner間の契約不一致をテストで検出する
  • clean-cluster E2Eの実行基盤を整備する

今回の変更では、これらの仕組みを構成するRepository全体を再編しました。

これまでのinfraには、KubernetesやCiliumなどの基盤構築処理に加え、AI Runner、Package Approver、Mirror、Git Bare、Proxy、Deployment Script、テストが配置されていました。

そのため、異なる責務の実装とテストが同じディレクトリに含まれていました。

今回の再編では、Application、AI Runtime、Deployment、Platformをそれぞれ独立した領域へ分割しています。


前回からの主な変更

項目前回今回
Repository構成appとinfraを中心に管理責務単位のコンポーネントへ分割
Backend / Frontendapp配下root直下へ分離
AI RunnerInfra配下のMJS実装workstation/runnerへ移動
Package GuardInfra内のApprover / Mirrorguard/へ独立
Git BareInfra側のGitサービスgit-bare/へ独立
ProxyInfra内で管理proxy/へ独立
Kubernetes ManifestInfra側を中心に配置deployment/へ集約
OpenAPIOpenAPIとConsumer側の契約定義を個別管理BackendのGo宣言から生成
Container ImageSchedulerとRunnerのBuild責務が混在Backend ImageとRunner Imageを分離
Runner SourceMonorepo全体を取得Ownership Policyに従い公開範囲を制限
テストInfra側に複数の責務が混在各コンポーネントとDeploymentへ再配置

Repositoryを責務単位へ分割する

再編後の主なディレクトリ構成は次のとおりです。

root/
├── backend/                 # Scheduler / Control Plane
├── frontend/                # Scheduler UI
│
├── workstation/             # AI実行環境
│   ├── runner/              # AI Jobの実行・検証・結果報告
│   ├── images/              # Runner用Container Image
│   └── capabilities/        # Runnerの実行能力
│
├── guard/                   # Package取得制御
│   ├── shared/
│   ├── npm/
│   ├── pypi/
│   └── go/
│
├── git-bare/                # 内部Git Repositoryサービス
├── proxy/                   # Scheduler向けmTLS Proxy
│
├── deployment/              # システム全体の配置と構成
│   ├── components/
│   ├── profiles/
│   ├── network/
│   ├── workspace/
│   ├── dependencies/
│   ├── repositories/
│   ├── scripts/
│   └── tests/
│
└── infra/                   # Cluster / Platform
    ├── bootstrap/
    ├── cilium/
    ├── certs/
    └── storage/

各ディレクトリが扱う責務を次のように分離しました。

ディレクトリ責務
backendTask、Job、Workspace、Runner API、状態管理
frontendScheduler UIとBackend APIの利用
workstationAI実行、Git操作、Validation、結果報告
guardPackage Approver、Mirror、依存取得の制御
git-bare内部Git Repositoryの提供
proxyScheduler向けmTLS通信の中継
deployment各サービスの配置、設定、依存関係、NetworkPolicy
infraCluster bootstrap、Cilium、証明書、Storage

BackendとFrontendの内部では、以前導入したレイヤー分割を引き続き使用しています。


DeploymentをApplication実装から分離する

今回の再編では、Applicationの実装とKubernetes上の配置定義を分離しました。

例えばBackendは、SchedulerのAPI、Domain、Application、Persistenceなどを実装します。

一方、次の情報はdeployment/で管理します。

  • Deployment / StatefulSet
  • Service
  • NetworkPolicy
  • PVC
  • Container Imageの指定
  • Runtime Profile
  • 環境別設定
  • Component間の配置依存関係
  • apply / verify / teardown手順

配置定義を各ApplicationのSource配下へ分散させず、システム全体のCompositionとして管理する構成です。

backend/             Scheduler実装
frontend/            UI実装
workstation/         AI Runtime実装
      │
      ▼
deployment/          配置定義・構成・実行手順
      │
      ▼
infra/               Cluster / Platform

これにより、Application実装と特定のKubernetes環境における配置設定が別々に管理されます。

DeploymentとInfraの境界

infra/は、Clusterを成立させるための処理を担当します。

主な対象は、bootstrap、Cilium、証明書、Storageです。

deployment/は、用意されたPlatformへAI Agent Workstationの各コンポーネントを配置するための定義と手順を担当します。

例えばCilium自体の導入処理はInfra側に配置し、BackendからGit Bareへの通信を許可するNetworkPolicyはDeployment側で管理します。


WorkspaceのProvisioningをDeploymentへ集約する

新しいWorkspaceやRunnerを追加する際の配置設定も、deployment/配下に整理しました。

deployment/profiles/ではRunnerごとのSource Directory、Build Context、Dockerfile、Manifestなどを管理します。

deployment/workspace/には、Workspaceの準備に関する処理を配置しています。

主な操作は次のように分かれています。

Cluster bootstrap
       ↓
Platform配置
       ↓
Certificate準備
       ↓
Scheduler配置
       ↓
Runner Provisioning
       ↓
WorkspaceとRunnerのBinding

Runnerの配置とWorkspaceのBindingは、別の操作として扱います。

また、Deployment側では各コンポーネントの配置に必要な依存関係を管理しています。

Application側の実装コードには、Namespace、NetworkPolicy、PVCなどの配置定義を含めない構成になっています。


BackendのGoコードからOpenAPIを生成する

前回までの実装では、SchedulerとRunner間のAPI契約をBackend OpenAPIとRunner側の定義で管理していました。

この構成では、同じRequestやResponseに対して異なるfield定義が存在する可能性があります。

実際に、以前のContract TestではProgressRequestのjob_idに関する定義差分が検出されていました。

今回の変更では、BackendのGoコードをAPI契約の正本として扱う構成に変更しました。

主な宣言は次のディレクトリに配置しています。

backend/internal/presentation/runnercontract/
├── types.go
├── schemas.go
├── operations.go
└── document.go

ここでは次の情報を定義します。

  • Request / Response DTO
  • Fieldの必須・任意
  • Nullable
  • Schemaの条件分岐
  • HTTP MethodとPath
  • Status Code
  • Error Response
  • 認証条件

これらの宣言からOpenAPIを生成します。

Backend Go宣言
       ↓
OpenAPI生成
       ↓
Runner用Contract Metadata生成
       ↓
Workstation Runner

生成物は次の場所に配置されます。

backend/openapi/
└── runner-v1.openapi.json

workstation/runner/
└── runner-contract.generated.mjs

RunnerはBackendのGo実装を直接参照せず、生成されたContract Metadataを利用します。

Contractの生成と検証

Contract関連の操作として、次のCommandを用意しています。

npm run contract:generate
npm run contract:check
npm run test:contract

contract:generateは契約生成物を更新します。

contract:checkはGo宣言から生成した結果とコミット済みの生成物を照合します。

test:contractでは、BackendのAPI契約とRunner側のConsumer契約を検証します。

これにより、BackendとRunnerのContractを個別に手書きする箇所を減らしています。


SchedulerとRunnerのContainer Imageを分離する

Container ImageのBuild定義も責務ごとに分離しました。

以前はWorkstation側のDockerfileが、SchedulerとRunnerの両方のBuildに関係していました。

現在、Scheduler ImageはBackend側に配置しています。

backend/
└── images/
    ├── Dockerfile.scheduler
    └── entrypoint.sh

Runner ImageはWorkstation側で管理します。

workstation/
└── images/
    └── Runner用Dockerfile群

Scheduler Imageには、Schedulerの実行に必要なApplicationと依存物を含めます。

Runner Imageには、AI CLI、Git、Validation Toolsなど、AI Jobの実行に必要なToolchainを含めます。

SchedulerのBuildとRunnerのBuildを分けることで、各Imageの依存関係も分離されています。

なお、ImageのBuild定義と、そのImageをどのKubernetes環境に配置するかという設定は別の領域で管理しています。


Runnerへ公開するGit Sourceを制限する

ディレクトリ分割に加えて、Runnerが取得できるGit Sourceの範囲も変更しました。

Monorepoを開発上の正本として維持しつつ、Runnerごとに公開するRepositoryを分離しています。

現在の主な公開範囲は次のとおりです。

RunnerRepository公開範囲
App Runnerapp.gitbackend/、frontend/、go.mod、go.sum
Infra Runnerinfra.gitinfra/

公開範囲の定義は次のファイルで管理します。

deployment/repositories/ownership.json

Git履歴も公開範囲に合わせて再構成する

GitのSparse Checkoutでは、Working Treeに表示されるファイルを限定できます。

ただし、Sparse CheckoutだけではRepositoryの履歴やObject Databaseに含まれる情報まで制限できません。

そこで、Runner公開用Repositoryの生成では、Ownership Policyで許可されたSourceだけを抽出し、独立したGit履歴を作成します。

Monorepo
    ↓
Ownership Policy
    ↓
許可されたBlob / Treeを抽出
    ↓
独立したCommitを生成
    ↓
app.git / infra.git
    ↓
Runner

元のMonorepo Commitを公開用Commitの親として引き継がない方式です。

そのため、Runnerが取得するRepositoryにMonorepo全体の過去履歴を含めずに済みます。

公開対象には、BranchだけでなくTagやTask Git Refも含まれるため、到達可能なGit Objectの範囲を確認します。

Source Ownershipの検証

Source公開処理では、次の条件を検査します。

  • 許可範囲外のSourceを公開しない
  • 元のMonorepo履歴を公開しない
  • 所有範囲外へ到達するSymlinkを拒否する
  • 不正なSubmoduleを拒否する
  • RepositoryとSource Scopeの不一致を拒否する
  • 未取り込みのRunner変更がある場合は再公開を拒否する
  • DirtyなSource Treeを拒否する

Runnerで作成された変更をMonorepoへ戻す際にも、公開時のBaseと現在のSourceを比較します。

公開時のBase
      +
Runnerの変更
      +
現在のMonorepo
      ↓
所有範囲・競合検証
      ↓
変更取り込み

取り込み時には、Runnerの所有範囲外にある変更を保持し、競合がある場合は操作を拒否します。

この仕組みは、Task Gitによる変更の作成・検証・取り込みと、Deploymentによる実環境への反映を別操作として扱っています。


Package Guard、Git Bare、Proxyを独立させる

Runner以外のサービス実装もInfraから分離しました。

Package Guard

Package取得制御はguard/へ移動しています。

guard/
├── shared/
├── npm/
├── pypi/
└── go/

npm、PyPI、GoのApproverとMirrorを管理します。

Git Bare

内部Git Repositoryサービスはgit-bare/へ移動しています。

Git daemon、Repository初期化、Hookなど、Gitサービスの実装を管理します。

Proxy

Proxyはproxy/へ分離しました。

現在の実装は、Scheduler向けのmTLS通信を中継するProxyです。

いずれもKubernetesへの配置定義とは分離し、サービス実装をそれぞれのディレクトリで管理しています。


テストの責務も再編する

Repositoryの変更に合わせて、テストをコンポーネントの責務ごとに再配置しました。

テスト領域検証対象
BackendAPI、認証、状態遷移、業務規則
WorkstationAI実行、Lease、Validation、Result Reporting
FrontendUI、Backend API Adapter
GuardPackage Approver、Mirror、公開制御
DeploymentImage構成、CLI配置、Source Ownership、Manifest
E2E複数コンポーネントを接続した実行

OpenAPI Consumer Testの重複実行も整理しています。

契約そのものの検証と、契約を利用する実装がContainer Imageへ正しく含まれるかという検証は、異なるテストとして扱います。

また、Deployment側ではManifestやNetworkPolicyの変更を検出するFingerprint検証を用意しています。

テスト結果

実行した主なテストの結果は次のとおりです。

対象結果
OpenAPI Consumer Contract3 / 3 PASS
Workstation Runner106 / 106 PASS
Guard63 / 63 PASS
Frontend133 / 133 PASS
Deployment Node Tests50 / 50 PASS
Workspace Python Tests29 / 29 PASS
Workspace Shell Tests4スクリプト成功
Source Ownership12 / 12 PASS

Source Ownershipの検証には、範囲外Sourceの公開拒否、Git履歴の制限、所有範囲外の変更の保持、競合時の拒否などを含みます。

なお、これらの結果にはGoテスト全体と実Browser E2Eの成功件数は含めていません。

E2E設計カタログは300ケースまで拡張されていますが、設計カタログの件数と実環境で実行済みの件数は区別しています。


まとめ

今回、AI Agent WorkstationのRepositoryを責務単位に再編しました。

主な変更は次のとおりです。

  • BackendとFrontendをroot直下へ分離
  • AI Runnerをworkstation/へ移動
  • Package Guard、Git Bare、Proxyを独立したディレクトリへ移動
  • Kubernetes Manifestと配置手順をdeployment/へ集約
  • infra/をCluster / Platformの構築責務へ整理
  • BackendのGo宣言からOpenAPIとRunner用Contract Metadataを生成
  • SchedulerとRunnerのContainer Imageを分離
  • Runnerへ公開するGit Sourceと履歴をOwnership Policyで制限
  • Runnerの変更を取り込む際の所有範囲・競合検証を追加
  • テストをコンポーネントと契約の責務ごとに再配置

前回はTask GitとRunner間の実行時契約を整理しました。

今回は、その仕組みを構成するRepository、Container Image、Deployment、Git Sourceの所有範囲を整理しています。

これにより、Backend、Workstation、Deployment、Infraがそれぞれ異なる責務を持つ構成となり、API契約とGit Sourceの公開範囲についても機械的な生成・検証を行う形へ変更されました。