Compare commits
No commits in common. "54245e3292bc4a625d7f6b976d9689bbe8559bbc" and "dfc5263eeceb3d3052cd171c068cb9a6bc5dffe2" have entirely different histories.
54245e3292
...
dfc5263eec
|
|
@ -6,21 +6,13 @@ updated_at: '2026-06-20T05:34:00Z'
|
|||
linked_tickets: ['00001KTR81P9X', '00001KV0SP0TY', '00001KVHR3WRF', '00001KVHR3WRY', '00001KVHR3WS6', '00001KVHR3WSD', '00001KVHR3WSN', '00001KVHR3WSW']
|
||||
---
|
||||
|
||||
## Goal
|
||||
## Objective
|
||||
|
||||
Add MCP local stdio integration to Yoi without weakening Worker history, prompt-context, scoped tool permission, or Plugin/Feature layering invariants.
|
||||
|
||||
MCP is a protocol-backed integration layer on top of `pod::feature`. `pod::feature` supplies contribution/lifecycle/runtime-discovered registration substrate; MCP owns its own enablement, local server trust model, command/env/secret policy, and MCP-specific permission decisions. MCP is not the Plugin model, and Plugin permission policy is not implemented by feature-layer authority grants.
|
||||
|
||||
## Motivation / background
|
||||
|
||||
Yoi needs to integrate with external capability providers without turning them into hidden context sources or bypassing ordinary Tool/Worker safety rules. MCP is useful because it can expose tools, resources, and prompts from local protocol servers, but those server-provided declarations and results are untrusted and must be normalized through Yoi's existing authority boundaries.
|
||||
|
||||
The first MCP slice should focus on local stdio servers because they are concrete enough to implement and debug while keeping remote auth, OAuth, Streamable HTTP, registry distribution, sampling, and elicitation out of the initial trust boundary.
|
||||
|
||||
A configured local MCP server runs as a local executable. Yoi feature authority does not sandbox that executable's OS-level side effects, so command/env/secret handling and explicit local trust policy are MCP-layer responsibilities rather than generic `pod::feature` grants.
|
||||
|
||||
## Strategy / design direction
|
||||
## Strategic direction
|
||||
|
||||
- Baseline the initial implementation on MCP specification `2025-11-25`.
|
||||
- Start with local stdio MCP servers only.
|
||||
|
|
@ -34,7 +26,7 @@ A configured local MCP server runs as a local executable. Yoi feature authority
|
|||
- Treat local stdio server execution as an explicit MCP config/trust decision, not as a `pod::feature` authority grant.
|
||||
- Document clearly that a configured local MCP server runs as a local executable; Yoi feature authority does not sandbox its OS-level side effects.
|
||||
|
||||
### Layering decisions
|
||||
## Layering decisions
|
||||
|
||||
- `pod::feature` is an API/contribution substrate.
|
||||
- It owns contribution declarations, provider/service lifecycle hooks, diagnostics, runtime-discovered registration plumbing, and integration with normal Worker/ToolRegistry paths.
|
||||
|
|
@ -46,7 +38,7 @@ A configured local MCP server runs as a local executable. Yoi feature authority
|
|||
- MCP enablement, command/env/secret handling, server trust, and MCP-specific permission decisions live in MCP config/implementation.
|
||||
- MCP provider-discovered tools/resources/prompts are exposed through the feature API and ordinary Yoi tool paths.
|
||||
|
||||
### Concrete implementation tickets
|
||||
## Concrete implementation tickets
|
||||
|
||||
Completed prerequisites:
|
||||
|
||||
|
|
@ -70,7 +62,7 @@ Concrete MCP implementation sequence:
|
|||
|
||||
The old broad implementation Ticket `00001KTR82RB7` is superseded by this sequence and should not be used as an implementation work item.
|
||||
|
||||
### Terminology
|
||||
## Terminology
|
||||
|
||||
Use `runtime-discovered` or `provider-discovered` for MCP tools/resources/prompts discovered from `tools/list`, `resources/list`, or `prompts/list`. Avoid `dynamic tools` / `dynamic registry` in new MCP design prose because those phrases imply that model-visible tool schemas may change during an active LLM run.
|
||||
|
||||
|
|
@ -83,7 +75,7 @@ run-stable for the duration of a model request/run;
|
|||
refreshed only at a safe boundary or reported as a diagnostic.
|
||||
```
|
||||
|
||||
### Later follow-ups
|
||||
## Later follow-ups
|
||||
|
||||
- Richer MCP task/task-support integration if ordinary tool-call fallback is insufficient.
|
||||
- Streamable HTTP transport.
|
||||
|
|
@ -91,7 +83,7 @@ refreshed only at a safe boundary or reported as a diagnostic.
|
|||
- Registry/package distribution.
|
||||
- Explicit MCP/Plugin bridge only if separately approved; do not conflate Plugin packages with MCP local server execution.
|
||||
|
||||
## Success criteria / exit conditions
|
||||
## Success criteria
|
||||
|
||||
- A local mock MCP server can be configured explicitly and initialized.
|
||||
- Discovered MCP tools appear as ordinary Yoi tools with stable namespacing.
|
||||
|
|
@ -101,11 +93,3 @@ refreshed only at a safe boundary or reported as a diagnostic.
|
|||
- Secret values, command/env details, and server diagnostics are redacted where required.
|
||||
- Local server trust boundary is documented: Yoi does not sandbox the configured executable through feature authority.
|
||||
- Feature, Plugin, and MCP permission/trust responsibilities are documented as separate layers.
|
||||
|
||||
## Decision context
|
||||
|
||||
- MCP is not the Plugin model; it is a protocol-backed integration layer using `pod::feature` substrate.
|
||||
- `pod::feature` should provide contribution/lifecycle/runtime-discovered registration plumbing, not MCP server trust policy or Plugin package permission policy.
|
||||
- MCP resources and prompts must never be hidden context injection. They are explicit operations recorded through ordinary history/tool paths.
|
||||
- Provider-discovered tools are discovered at startup/provider initialization and registered before model exposure; model-visible schemas remain run-stable during a request/run.
|
||||
- Local stdio server execution is a user/config trust decision. Yoi does not sandbox the local executable merely because it is configured through MCP.
|
||||
|
|
|
|||
|
|
@ -1,240 +0,0 @@
|
|||
---
|
||||
title: "Team workspace control plane and runner architecture"
|
||||
state: "active"
|
||||
created_at: "2026-06-20T14:26:29Z"
|
||||
updated_at: "2026-06-20T15:28:00Z"
|
||||
linked_tickets: []
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Yoi を、単一のローカル開発ディレクトリで動くエージェント実行ツールから、チームで作業・判断・実行結果を管理できるワークスペース基盤へ発展させる。
|
||||
|
||||
この Objective の中心は、Web から扱える管理システムを作り、その管理システムにローカル実行環境・リモート実行環境・将来のクラウド実行環境を接続できるようにすることである。管理システムは Ticket、Objective、Memory、Knowledge、Run、Artifact、Runner の正本を持つ。実行環境はその管理システムから仕事を受け取り、コード取得、作業用ディレクトリ作成、エージェント実行、検証、結果報告を行う。
|
||||
|
||||
この Objective は Git ホスティングサービスを作るものではない。Git は重要な Repository provider として扱うが、Yoi の Workspace は Git Repository root と同じものにしない。Yoi が作るべきものは、コード・ドキュメント・データ・成果物などの Repository と実行環境を接続しながら、人間とエージェントの作業、Ticket lifecycle、Memory/Knowledge、検証証跡、実行環境配置を管理するチームワークスペースである。
|
||||
|
||||
## Glossary
|
||||
|
||||
この Objective では、以下の語をこの意味で使う。
|
||||
|
||||
- Workspace: チームまたはプロジェクトの管理単位。Ticket、Objective、Memory、Knowledge、Run、Artifact、Policy、Actor、Repository を持つ。Git Repository root ではない。
|
||||
- Control plane: Workspace の正本を持ち、Web UI / API / CLI から操作される管理システム。
|
||||
- Runner: Control plane から仕事を受け取り、実際にエージェントやツールを動かす実行環境。最初はローカルマシン上の runner、後でリモート runner やクラウド runner を追加する。
|
||||
- Repository: Workspace に接続される source/storage。コード、ドキュメント、local directory、object storage、artifact store、dataset などを含む。Git Repository も Repository の一種であり、基本的には filesystem path ではなく URI / URL で識別する。
|
||||
- Repository provider: Repository の種類ごとの実装。Git、local filesystem、object store、artifact store、将来の non-Git VCS など。
|
||||
- RepositoryPoint: Repository 内の特定地点。Git commit/ref/path、object store version/prefix、file snapshot/path など、provider ごとの revision/ref/snapshot/path を表す。
|
||||
- Execution Workspace: Runner が Run のために作る作業用ディレクトリや container filesystem。1 つ以上の RepositoryPoint から materialize される。Git worktree、clone、sparse checkout などはこれを作る手段である。
|
||||
- Ticket: チームで扱う作業単位。目的、要件、判断、議論、完了条件、関係、証跡を持つ。
|
||||
- Objective: 複数の Ticket を束ねる長期目標や設計方針。
|
||||
- Run: Ticket や Objective に対して行われた具体的な実行試行。どの Runner が、どの Execution Workspace で、何を実行し、どんな結果になったかを持つ。
|
||||
- Artifact: Run や Ticket に紐づく成果物や証跡。diff、log、validation result、review result、report など。
|
||||
- Memory: エージェントやユーザーが再利用するための要約された文脈。Ticket や Run の正本ではない。
|
||||
- Knowledge: 保守された知識や設計判断。Memory より人間が維持する資料に近い。
|
||||
- Actor: 人間、エージェント、システム、外部サービスなど、Workspace 上で操作や発言を行う主体。
|
||||
|
||||
## Motivation / background
|
||||
|
||||
現在の Yoi は、ローカルの `.yoi` ディレクトリ、ローカルプロセス、Ticket ファイル、ワークツリー運用によって、自分自身の開発に使えるエージェント実行環境になっている。しかし、チーム利用、Web UI、リモート実行、クラウド実行、最終的な SaaS 提供を考えると、次の前提を変える必要がある。
|
||||
|
||||
- Workspace を Git Repository root と同一視しない。
|
||||
- ローカル filesystem 上の `.yoi` を、長期的なチーム用正本 store にしない。
|
||||
- Ticket をローカル作業メモではなく、チームの作業調整 record にする。
|
||||
- Ticket と実行試行を分ける。実行試行は Run として記録する。
|
||||
- 管理システムと実行環境を分ける。
|
||||
- まず Web から Ticket、Objective、Memory、Knowledge、Run、Artifact、Runner state を見られるようにする。
|
||||
- 最初はローカルマシンを Runner として使い、後でリモート Runner、クラウド Runner、runner pool、resource allocation、quota、billing、sandboxing に拡張する。
|
||||
- Git ホスティング機能を取り込むのではなく、Git Repository / worktree / clone は Repository provider と Execution Workspace materialization の手段として扱う。
|
||||
|
||||
OSS として Control plane、Runner、Web frontend、protocol を公開しつつ、managed service では hosted control plane、runner fleet、リソース柔軟性、team auth、backup、audit、availability、multi-tenant operations で価値を出す。
|
||||
|
||||
## Strategy / design direction
|
||||
|
||||
### 1. Control plane を先に作る
|
||||
|
||||
Team Workspace の正本は server-side control plane に置く。`.yoi` は local backend、single-user/self-hosted compatibility、offline/export/import、runner-local projection、migration bridge として残せるが、multi-user SaaS の正本とはみなさない。
|
||||
|
||||
Control plane は Ticket、Objective、Memory、Knowledge、Run、Artifact、Actor、Permission、Audit、Runner state を管理する。Web UI、CLI、TUI、将来の desktop client は、この Control plane を操作する client であり、別の正本 store を持たない。
|
||||
|
||||
### 2. Workspace と Repository を同一視しない
|
||||
|
||||
Workspace はチームまたはプロジェクトの作業管理単位である。Repository は Workspace に接続される source/storage である。Git Repository は Repository の一種にすぎない。
|
||||
|
||||
1 つの Workspace は複数の Repository を持てる。Repository は filesystem path ではなく URI / URL で識別する。例として `git+https://...`、`file://...`、`s3://...`、`artifact://...`、将来の VCS provider URI などを扱えるようにする。
|
||||
|
||||
Ticket と Objective は Repository 配下に置かず、Workspace 配下に平たく持つ。Ticket は必要に応じて対象 Repository、ref selector、path、必要 capability を持つ。Objective は複数 Ticket にまたがる target default / scope hint を持てるが、Repository の所有物にはしない。
|
||||
|
||||
Run は Ticket の target selector を具体的な RepositoryPoint に解決し、その RepositoryPoint から Execution Workspace を materialize する。Git worktree 相当の機能は、この Execution Workspace を作るための実装戦略として扱う。
|
||||
|
||||
短期的には Git を主な Repository provider とする。ただし Yoi の authority model を Git object、Git branch、Git Repository root、worktree path に固定しない。Orchestration は Git そのものではなく、`resolve_ref`、`materialize`、`diff`、`patch`、`commit`、`merge` などの Repository capability に依存する。
|
||||
|
||||
### 3. Ticket を team coordination record にする
|
||||
|
||||
Ticket は実行そのものではない。Ticket は「何を、なぜ、どの条件で完了とみなすか」を持つ。Ticket は Workspace に平たく所属し、Repository には所属しない。コードやドキュメントを対象にする Ticket は、対象 Repository / ref selector / path / intent を target として持つ。
|
||||
|
||||
Ticket target は intent/selector であり、実行再現性のための immutable point ではない。Run が target selector を concrete RepositoryPoint に解決し、実際にどの revision/snapshot を materialize したかを記録する。
|
||||
|
||||
```text
|
||||
Ticket
|
||||
-> target selectors: Repository + ref selector + path + intent
|
||||
-> Run / Attempt
|
||||
-> resolved RepositoryPoint
|
||||
-> Execution Workspace
|
||||
-> Artifact / Evidence
|
||||
-> Review / Decision
|
||||
-> Audit / Notification
|
||||
```
|
||||
|
||||
Target 例:
|
||||
|
||||
```text
|
||||
Ticket targets:
|
||||
- repository: main-code
|
||||
role: primary
|
||||
ref: develop
|
||||
paths: ["crates/pod/"]
|
||||
intent: change
|
||||
- repository: docs
|
||||
role: related
|
||||
ref: main
|
||||
paths: ["docs/development/"]
|
||||
intent: read
|
||||
|
||||
Run inputs:
|
||||
- repository: main-code
|
||||
requested_ref: develop
|
||||
resolved_point: git commit abc123
|
||||
mount: /workspace/main-code
|
||||
```
|
||||
|
||||
Ticket には次の概念が必要になる。
|
||||
|
||||
- Actor identity: human / agent / system / service account.
|
||||
- Assignment / owner / reviewer / watcher.
|
||||
- Typed thread events: comment, decision, plan, review, implementation report, state transition.
|
||||
- Linked Objective / Artifact / Run / Repository / RepositoryPoint / Execution Workspace.
|
||||
- Permission / visibility.
|
||||
- Audit trail.
|
||||
- Notification / mention.
|
||||
- Board / queue / planning / review / done / archived views.
|
||||
- Conflict handling and concurrent editing policy.
|
||||
|
||||
### 4. Memory / Knowledge を team 用に再設計する
|
||||
|
||||
Memory / Knowledge は Ticket / Run / Artifact のコピーではない。再利用可能な文脈、方針、学習された制約、保守された知識として扱う。
|
||||
|
||||
最低限、次を分ける。
|
||||
|
||||
- Personal Memory: 個人の好みや作業スタイル。
|
||||
- Workspace Memory: チームまたはプロジェクトの方針、繰り返し出る制約、設計判断。
|
||||
- Run Summary: 個別 Run や session の要約。必ず provenance を持つ。
|
||||
- Maintained Knowledge: 人間が保守する資料、設計判断、API notes、運用知識。
|
||||
- Ticket / Objective / Run / Artifact: 作業と実行結果の正本。
|
||||
|
||||
Generated Memory には provenance、visibility、approval、audit を持たせる。Memory は Ticket や Run audit log の代替にしない。
|
||||
|
||||
### 5. 管理システムと実行環境を弱結合にする
|
||||
|
||||
Control plane は正本と調整を持つ。Runner は実行を担当する。
|
||||
|
||||
初期形:
|
||||
|
||||
```text
|
||||
Web UI / Control Plane
|
||||
-> Runner connection
|
||||
-> Local machine runner
|
||||
-> Existing Yoi runtime, tools, working copy, build/test commands
|
||||
```
|
||||
|
||||
この段階では、現在ローカル管理画面が行っている Ticket 選択、エージェント起動、レビュー起動、作業用 checkout 作成、検証実行、結果表示を、Web/control plane から local runner に対して実行できるようにする。
|
||||
|
||||
その後で、remote runner、self-hosted runner、hosted cloud runner、runner pool、resource allocation、quota、billing、sandbox、network policy、secret distribution を追加する。
|
||||
|
||||
```text
|
||||
Phase 1: Web control plane + local runner
|
||||
Phase 2: Remote/self-hosted runner
|
||||
Phase 3: Hosted cloud runner fleet
|
||||
Phase 4: Resource allocation / scheduling / quotas / billing / isolation
|
||||
```
|
||||
|
||||
### 6. Web frontend を先に作る
|
||||
|
||||
Desktop app は対応コストが高いので、まず Web frontend を primary UI とする。
|
||||
|
||||
- Web: チームで使う主要 UI。
|
||||
- CLI: automation、scripting、local operations。
|
||||
- TUI/local panel: local runner cockpit、fallback、dogfooding surface。
|
||||
- Future desktop: Web/control-plane model が安定した後に検討する optional client。
|
||||
|
||||
Web UI は Ticket、Objective、Memory、Knowledge、Run、Runner、Artifact を扱う。UI の都合で正本を二重化しない。
|
||||
|
||||
### 7. 多重起動コストと runtime placement を見直す
|
||||
|
||||
Cloud/remote execution を成立させるには、多数のエージェント実行を安く管理できる必要がある。logical agent session と runtime process/resource placement を分ける。
|
||||
|
||||
検討対象:
|
||||
|
||||
- Agent identity と process/runtime placement の分離。
|
||||
- Provider client、tool registry、resource cache の共有可能性。
|
||||
- Prompt/resource/profile resolution cache。
|
||||
- Model call multiplexing and scheduling。
|
||||
- Tool execution sandbox reuse。
|
||||
- Plugin instance / Service runtime との統合。
|
||||
- Session/event stream と runtime lifecycle の分離。
|
||||
- Runner-local cache、checkout reuse、build cache、dependency cache。
|
||||
|
||||
## Initial phases / candidate tickets
|
||||
|
||||
1. **Vocabulary / architecture record**
|
||||
- Workspace / Repository / RepositoryPoint / Execution Workspace / Runner / Control Plane / Run / Ticket / Memory / Knowledge の用語と境界を固める。
|
||||
2. **Team-space canonical data model**
|
||||
- Ticket / Objective / Target / Run / Artifact / Actor / Permission / Audit / Memory / Knowledge の entity/event model を設計する。
|
||||
3. **Ticket and Run separation**
|
||||
- Ticket lifecycle と execution attempt / orchestration run / validation run を分離し、Ticket thread と Run evidence の責務を明確化する。
|
||||
4. **Memory model for team workspace**
|
||||
- Personal / Workspace / Run Summary / Maintained Knowledge / provenance / visibility / approval を設計する。
|
||||
5. **Control plane backend architecture**
|
||||
- local `.yoi` backend と server-side canonical backend の境界、migration/export/import、compatibility mode を設計する。
|
||||
6. **Web control plane MVP design**
|
||||
- read-only Ticket / Objective / Memory / Knowledge / Runner state UI/API の範囲を決める。
|
||||
7. **Local runner protocol design**
|
||||
- Web/control plane から local runner に安全な操作を送る protocol と authority boundary を設計する。
|
||||
8. **Repository and Execution Workspace materialization model**
|
||||
- Repository URI、Repository provider capability、RepositoryPoint resolution、Git worktree / clone / sparse checkout / future source backend を runner-side strategy として抽象化する。
|
||||
9. **Remote/hosted runner foundation**
|
||||
- runner registration, heartbeat, capability advertisement, job assignment, logs/events, secrets, sandbox/resource policy を設計する。
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Git hosting service を作ること。
|
||||
- `.yoi` filesystem をそのまま SaaS canonical store にすること。
|
||||
- 最初から full hosted cloud execution を作ること。
|
||||
- local execution / CLI / TUI / local panel を捨てること。
|
||||
- Ticket を単なる issue tracker clone にすること。
|
||||
- Memory を Ticket/Run audit log の代替にすること。
|
||||
- Web UI のために core authority を二重化すること。
|
||||
- hidden server state を LLM context に直接注入すること。
|
||||
- multi-tenant auth/billing/secret/security を shortcut して実装すること。
|
||||
|
||||
## Success criteria / exit conditions
|
||||
|
||||
- Workspace / Repository / RepositoryPoint / Execution Workspace / Runner / Control Plane / Run / Ticket / Memory / Knowledge の境界が文書化されている。
|
||||
- Ticket が team coordination record として、target selector / Run / Artifact / Actor / Permission / Audit と分離された model を持つ。
|
||||
- `.yoi` local backend は compatibility/local backend として整理され、server-side canonical backend の設計を阻害しない。
|
||||
- Web UI/API が Ticket / Objective / Memory / Knowledge / Runner state の read-only view を提供できる設計または MVP を持つ。
|
||||
- Control plane から local runner に対して、現在のローカル管理画面相当の安全な操作を実行できる design/protocol がある。
|
||||
- Git Repository root に依存しない Workspace model があり、Git Repository は Repository provider の一種として扱われている。
|
||||
- Ticket と Objective は Workspace 配下に平たく存在し、Repository への所属ではなく target selector / scope hint で対象を表現する。
|
||||
- Git worktree 相当は Execution Workspace materialization strategy として扱われ、Run が immutable な RepositoryPoint を記録する。
|
||||
- Memory / Knowledge の personal/workspace/run-summary/provenance/visibility 方針が決まっている。
|
||||
- Hosted runner / resource allocation / SaaS offering に進むための後続 Ticket が切れる状態になっている。
|
||||
- 既存 local dogfooding runtime を壊さず、local use と remote-capable architecture が両立している。
|
||||
|
||||
## Decision context
|
||||
|
||||
- Yoi は hosted Git tool ではなく、team workspace control plane + execution environment として設計する。
|
||||
- Team-space の長期 canonical authority は server-side control plane に置く。local `.yoi` は互換/local/offline/export/import surface だが、multi-user SaaS の正本ではない。
|
||||
- 実行環境と管理システムは弱結合にする。まず管理システムを独立させ、local runner を実行環境として接続する。その後に remote/self-hosted/hosted runner fleet へ進む。
|
||||
- Web frontend を最初の primary team UI とする。Desktop app は web/control-plane model が安定した後に検討する。
|
||||
- Git は重要な Repository provider / materialization backend として使うが、Workspace identity と authority を Git Repository root に固定しない。
|
||||
- Ticket と Objective は Workspace 配下に平たく持つ。対象コードベースや ref は Repository target selector として表現し、Run が concrete RepositoryPoint に解決する。
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
---
|
||||
title: "効果的な Memory システム設計・検証"
|
||||
state: "active"
|
||||
created_at: "2026-06-20T15:16:00Z"
|
||||
updated_at: "2026-06-20T15:16:00Z"
|
||||
linked_tickets: ["00001KSKBPHRG", "00001KT02TCCG", "00001KTGCAFXG", "00001KSKBPTHR"]
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Yoi の Memory / Knowledge / generated memory / resident context / retrieval / usage metrics を、実際の開発・設計・レビュー・オーケストレーションに効く sensemaking substrate として再設計・検証する。
|
||||
|
||||
この Objective でいう「効果的な Memory システム」は、単に多く保存する仕組みではなく、作業中の問いに対して relevant material を集め、根拠を検証可能にし、再表現・仮説形成・反証探索・意思決定・成果物への反映を低コストにする仕組みである。
|
||||
|
||||
暫定的な定義:
|
||||
|
||||
- foraging cost を下げる: Ticket / Objective / current question に対して、関連する memory / docs / tickets / session evidence / code references を探しやすい。
|
||||
- evidence を失わない: Memory が authority そのものにならず、Ticket / docs / git history / session logs / user instruction への検証可能な入口になる。
|
||||
- schema 化を支援する: raw summary ではなく、subsystem、invariant、risk、authority boundary、open question、rejected alternative、hypothesis など推論しやすい形へ再表現できる。
|
||||
- hypothesis loop を支援する: 支持証拠だけでなく、代替仮説・棄却理由・反証 evidence・stale assumption を扱える。
|
||||
- product に戻る: Memory に保存して終わりではなく、Ticket、review、docs、implementation、decision、report に影響を戻せる。
|
||||
- stale / contradiction を扱う: 古い前提、矛盾、適用範囲外の memory を検出・降格・更新できる。
|
||||
- usage を成果基準で測る: resident exposure や read count ではなく、判断・レビュー・実装・docs に効いたかを観測できる。
|
||||
|
||||
## Motivation / background
|
||||
|
||||
現在の Memory システムは「墓場化」している。保存された情報はあるが、後続の作業で自然に使われにくく、使われたとしても根拠・適用範囲・鮮度・反証可能性が弱い。結果として Memory は、作業場ではなく古い結論の倉庫になりやすい。
|
||||
|
||||
Pirolli & Card 2005 の sensemaking model では、分析作業は単なる保存ではなく、次の変換として捉えられる。
|
||||
|
||||
```text
|
||||
external data sources
|
||||
-> shoebox
|
||||
-> evidence file
|
||||
-> schema / representation
|
||||
-> hypotheses
|
||||
-> presentation / product
|
||||
```
|
||||
|
||||
Yoi の現行 Memory は、この流れのうち「保存」と「一部の検索」には対応しているが、少なくとも以下が弱い。
|
||||
|
||||
- Ticket / task / question ごとの shoebox がない。
|
||||
- shoebox から evidence snippets を切り出し、source / provenance / applicability / confidence と共に扱う evidence file がない。
|
||||
- `summary`, `decision`, `request`, `knowledge` は storage taxonomy であり、sensemaking 用 schema としては粗い。
|
||||
- decision は残るが、hypothesis space、alternative、rejected reason、disconfirming evidence が残りにくい。
|
||||
- reviewer / orchestrator が confirmation bias を避けるための反証探索導線が弱い。
|
||||
- resident exposure と explicit retrieval は観測できても、Memory が product に効いたかは測りにくい。
|
||||
|
||||
この Objective は、Memory 関連の設計・検証・検討・考察を一元化し、個別 Ticket がばらばらに storage、prompt、retrieval、metrics を改善して再び墓場を増やすことを防ぐための判断背景である。
|
||||
|
||||
## Strategy / design direction
|
||||
|
||||
Memory を「長期保存領域」ではなく、Yoi の multi-agent 開発における sensemaking loop の支援機構として設計する。
|
||||
|
||||
### 1. Pirolli & Card の stage に合わせて責務を分ける
|
||||
|
||||
- external data sources: Tickets、docs、git history、session logs、reports、code、user instructions。
|
||||
- shoebox: 特定 Ticket / Objective / design question に対して関連しそうな材料を集めた task-bound working set。
|
||||
- evidence file: shoebox から抜き出した根拠 snippet。source anchor、支持/反証、適用範囲、confidence、staleness を持つ。
|
||||
- schema / representation: subsystem、invariant、risk、authority boundary、open question、hypothesis、alternative、contradiction など、推論しやすい再表現。
|
||||
- hypotheses: 採用前の設計仮説、代替案、棄却条件、反証 evidence。
|
||||
- product: Ticket、review、docs、implementation、decision、report、orchestration plan などの成果物。
|
||||
|
||||
### 2. 最初の重点は task-bound shoebox と evidence file
|
||||
|
||||
Memory 墓場化の最初の原因は、保存情報が現在の問いに集まらないことである。まずは Orchestrator / Intake / Reviewer が Ticket を扱う時に、関連 memory / docs / tickets / reports / prior decisions を shoebox として束ねる導線を作る。
|
||||
|
||||
この段階では大きな永続 schema 追加に飛びつかず、report / Ticket artifact / bounded generated context として検証してよい。
|
||||
|
||||
### 3. Memory を authority にしない
|
||||
|
||||
Memory は Ticket、docs、git history、session logs、user instruction の代替ではない。Memory は authority record への evidence index / schema / reasoning aid として扱う。
|
||||
|
||||
したがって、改善案は次の性質を持つべきである。
|
||||
|
||||
- source / provenance を辿れる。
|
||||
- stale / superseded / contradicted を扱える。
|
||||
- Memory の断定をそのまま authority として使わない。
|
||||
- Ticket body/thread/artifacts を読まずに Objective や Memory だけで実装判断できる状態を作らない。
|
||||
|
||||
### 4. 反証探索を first-class にする
|
||||
|
||||
より効果的な Memory は、過去方針を思い出すだけでなく、現在案を疑うために使える必要がある。
|
||||
|
||||
Reviewer / Orchestrator / Intake の導線では、次を探せるようにする。
|
||||
|
||||
- supporting evidence
|
||||
- contradicting evidence
|
||||
- stale decisions
|
||||
- rejected alternatives
|
||||
- unresolved questions
|
||||
- authority boundary risks
|
||||
- prior failures / reports
|
||||
|
||||
### 5. Metrics は exposure から product impact へ寄せる
|
||||
|
||||
Memory が prompt に入った、または query されたことは成功ではない。評価は次を区別する。
|
||||
|
||||
- resident exposure
|
||||
- explicit retrieval
|
||||
- cited in response
|
||||
- cited in Ticket / review / report
|
||||
- changed requirement
|
||||
- changed implementation
|
||||
- contradicted / invalidated
|
||||
- led to docs or decision update
|
||||
|
||||
### 6. 後続 Ticket は concrete slice に分割する
|
||||
|
||||
この Objective は中期的な設計・検証の一元化 record であり、umbrella Ticket ではない。実装や調査は、単独で実装・レビュー・close できる concrete Ticket に分割する。
|
||||
|
||||
候補 slice:
|
||||
|
||||
- Memory sensemaking 分析 report を `docs/report/` に作る。
|
||||
- Ticket routing 用 Memory shoebox artifact を試作する。
|
||||
- evidence snippet schema / source resolver を設計する。
|
||||
- hypothesis / rejected alternative / disconfirming evidence の表現を追加する。
|
||||
- Reviewer workflow に反証探索を入れる。
|
||||
- Memory usage metrics を product impact oriented に拡張する。
|
||||
- stale / contradiction / renewal の検出・表示を設計する。
|
||||
|
||||
## Success criteria / exit conditions
|
||||
|
||||
- Memory システムの目的が「保存」ではなく「sensemaking loop 支援」として project records / docs / prompts / workflows で一貫して説明されている。
|
||||
- Pirolli & Card の `shoebox -> evidence file -> schema -> hypotheses -> product` に対応する Yoi 内の責務と非責務が整理されている。
|
||||
- Ticket / Objective / docs / session logs / Memory / Knowledge の authority boundary が明確で、Memory が authority を僭称しない。
|
||||
- 少なくとも一つの実作業 routing / review / design analysis で、task-bound shoebox または evidence file が生成・利用され、作業品質にどう効いたかが確認されている。
|
||||
- Memory records または関連 artifacts が source / provenance / applicability / staleness / supports-or-refutes のいずれかを扱えるようになっている。
|
||||
- Reviewer / Orchestrator が supporting evidence だけでなく、contradicting evidence / stale assumptions / rejected alternatives を探す導線を持っている。
|
||||
- Memory usage metrics が resident exposure と product impact を区別している。
|
||||
- 古い Memory が放置されるのではなく、stale / superseded / contradicted / needs-review として扱える方針がある。
|
||||
- 後続の実装 Ticket が concrete slice として分割され、Objective が Ticket dependency や進捗 container として使われていない。
|
||||
|
||||
この Objective は、Memory が少なくとも一つの中規模設計・実装・レビュー作業で「関連情報を見つける」「根拠を確認する」「代替案/反証を検討する」「成果物へ反映する」流れを実証し、その設計方針が docs / workflows / metrics に反映された時点で `done` を検討できる。
|
||||
|
||||
## Decision context
|
||||
|
||||
- ユーザー指摘: 「Memoryシステムが完全に墓場化している」。これは保存量不足ではなく、保存情報が現在の問い・根拠・仮説・成果物に接続されない問題として扱う。
|
||||
- ユーザー指示: Memory システムの設計・検証・検討・考察を Objective にまとめ、より効果的な Memory システムを作成する目標のもとで情報を一元化する。
|
||||
- 「効果的」の定義は未確定だが、当面は Pirolli & Card の sensemaking process に沿って、foraging cost、evidence quality、schema usefulness、hypothesis/disconfirmation support、product impact、staleness handling を評価軸にする。
|
||||
- Memory は durable project authority ではない。Ticket、docs、git history、session logs、明示 user instruction の代替として使わない。
|
||||
- Objective context は判断背景であり、個別実装の authority は各 Ticket body/thread/artifacts と明示的な Ticket relations / OrchestrationPlan records にある。
|
||||
- `history` に残らない context-only injection を改善案にしない。新しい context input は history に commit する原則を守る。
|
||||
- Knowledge は単なる長期保存ではなく、再利用可能な schema / model / procedure / invariant として再検討する余地がある。
|
||||
- Generated memory / curated Knowledge / Ticket / docs / report の境界を再定義する場合は、authority boundary と migration/staleness を明示する。
|
||||
- 関連する既存 Ticket:
|
||||
- `00001KSKBPHRG` — Prompt / Workflow 評価メトリクスと改善 Offer
|
||||
- `00001KT02TCCG` — Memory prompt: conditional guidance and proactive lookup
|
||||
- `00001KTGCAFXG` — Use .yoi/memory marker for repo-local memory root
|
||||
- `00001KSKBPTHR` — ワークスペースのメモリーをLintするヘッドレスCLI
|
||||
|
||||
## Historical references / prior design sources
|
||||
|
||||
現在の Memory システムの初期設計時には、Codex Memories / Chronicle と HermesAgent を明示的な参考事例として調査していた。関連する調査・設計記録は、現在は主に以下に退避されている。
|
||||
|
||||
- `docs/.local/old-docs/ref/memory-systems.md`
|
||||
- `docs/.local/old-docs/plan/memory.md`
|
||||
- 初期設計 commit: `ca5a3d11` — `2026-04-21 メモリシステムの設計`
|
||||
- 関連 commit:
|
||||
- `0c1276b7` — `Memoryシステムの整理・Promptカタログチケット`
|
||||
- `3d04f793` — `memoryを抽出する仕組みの実装`
|
||||
- `f1b7af62` — `docs: memoryシステムの仕様変更と、動的Tool・VCSの話`
|
||||
- `a2aecbf0` — `update: memoryシステムの"Phase"表記を撤廃`
|
||||
|
||||
### Codex Memories / Chronicle から得た設計要素
|
||||
|
||||
旧設計では、Codex Memories / Chronicle を `extract -> staging -> consolidation -> durable Markdown memory` の非同期パイプラインとして捉えていた。
|
||||
|
||||
主な参照点:
|
||||
|
||||
- extract と consolidation の 2 段構成。
|
||||
- extract は JSON schema / structured output で分類ブレを抑える。
|
||||
- consolidation は reasoning model / agentic rewrite によって、既存 memory と staging entries を統合・整理する。
|
||||
- staging と durable memory を分ける。
|
||||
- `MEMORY.md` は retrieval-oriented handbook として扱う。
|
||||
- `memory_summary.md` は prompt-loaded high-signal context として扱う。
|
||||
- `raw_memories.md` は routing layer / task inventory 的な中間層として扱う。
|
||||
- workspace diff や usage 情報を使い、stale / deleted evidence / noisy entries を整理する。
|
||||
- consolidation は append だけでなく、rewrite / merge / split / trim / drop / cleanup を担う。
|
||||
|
||||
Yoi 初期設計では、これを参考に以下を意図していた。
|
||||
|
||||
- activity token 閾値で extract を発火する。
|
||||
- compact より前に session log range を抽出する。
|
||||
- extract は `decisions`, `discussions`, `attempts`, `requests` などの候補を staging に保存する。
|
||||
- 抽出時点では Knowledge 化せず、純粋な「起きたこと」に寄せる。
|
||||
- consolidation が summary / decisions / requests / knowledge candidates を整理する。
|
||||
- consolidation 入力に linter warnings / usage metrics / Knowledge 化候補を含める。
|
||||
- stale / superseded / unused / noisy な情報を整理する。
|
||||
|
||||
この Objective での再解釈:
|
||||
|
||||
- Codex の `raw_memories.md` は、Pirolli & Card の sensemaking model では `shoebox` または `evidence file` に近い。
|
||||
- Yoi は extract / consolidation という pipeline だけを継承しても不十分であり、task-bound shoebox / evidence file / hypothesis loop / product feedback がなければ Memory は再び墓場化する。
|
||||
- 特に、staging を consolidation の一時入力としてだけ扱うと、後続 Ticket / Objective / review が使う探索入口にならない。
|
||||
- Yoi では `raw memories` 相当の中間層を、現在の問いに紐づく working set / evidence index として再設計する必要がある。
|
||||
|
||||
### HermesAgent から得た設計要素
|
||||
|
||||
旧設計では、Nous Research HermesAgent を 3 層の memory system として整理していた。
|
||||
|
||||
- Persistent Memory:
|
||||
- `MEMORY.md` / `USER.md`
|
||||
- Markdown + SQLite / FTS5 session search
|
||||
- 起動時 system prompt snapshot
|
||||
- bounded character limits
|
||||
- Skill Library:
|
||||
- procedural memory
|
||||
- `~/.hermes/skills/<name>/SKILL.md`
|
||||
- `skill_manage` tool による agentic CRUD
|
||||
- User Model / Honcho:
|
||||
- dialectic user modeling
|
||||
- 外部 service 連携
|
||||
|
||||
HermesAgent で特に重要だった点:
|
||||
|
||||
- memory / skill review は一定 turn / tool iteration ごとに background agent として起動する。
|
||||
- 保存すべきものがなければ `Nothing to save.` で NOP として終了する。
|
||||
- Yoi extract の「空配列許容」はこの設計からも影響を受けている。
|
||||
- memory は session start 時の frozen snapshot として system prompt に入り、mid-session write で prompt cache を壊さない。
|
||||
- persistent memory は bounded で、limit 超過時は deterministic eviction ではなく、agent に replace / remove を促す。
|
||||
- procedural memory / skills は一般 memory から分離されている。
|
||||
- SQLite FTS5 + LLM summarization による cross-session recall がある。
|
||||
|
||||
この Objective での再解釈:
|
||||
|
||||
- HermesAgent の `MEMORY.md` / `USER.md` / `skills` の分離は、Yoi の Knowledge / Workflow / prompt resource / docs / Ticket decision / generated memory の責務再整理に使える。
|
||||
- reusable procedure, reviewer focus, orchestration tactic, project preference, user preference, design invariant を同じ Memory bucket に入れると墓場化しやすい。
|
||||
- `Nothing to save.` / empty extraction allowed は重要だが、保存抑制だけでは効果的な Memory にはならない。保存されたものが task-bound shoebox / evidence / schema / hypothesis / product に接続される必要がある。
|
||||
- frozen snapshot / prompt cache 配慮は Yoi の history/context 加工原則と整合するが、それだけでは retrieval / resurfacing / disconfirmation は解決しない。
|
||||
|
||||
### Lessons for the next design iteration
|
||||
|
||||
Codex と HermesAgent の調査から、Yoi が継承すべきものと、継承するだけでは足りないものを分ける。
|
||||
|
||||
継承すべきもの:
|
||||
|
||||
- structured extract と agentic consolidation の分離。
|
||||
- staging / raw memories / durable memory の分離。
|
||||
- 保存対象がなければ NOP にする発火設計。
|
||||
- prompt-loaded summary と durable retrieval-oriented memory の分離。
|
||||
- stale / noisy / unused entries の cleanup。
|
||||
- procedural memory と declarative memory の分離。
|
||||
- session search / usage metrics / linter feedback を consolidation に入れる設計。
|
||||
|
||||
足りないもの:
|
||||
|
||||
- Pirolli & Card の sensemaking stage における各 record の役割定義。
|
||||
- Ticket / Objective / current question に紐づく task-bound shoebox。
|
||||
- authority record へ戻れる evidence file / provenance / source anchor。
|
||||
- hypothesis, alternative hypothesis, rejected reason, disconfirming evidence の first-class 表現。
|
||||
- reviewer / orchestrator が confirmation bias を避けるための反証探索導線。
|
||||
- resident exposure や read count ではなく product impact を測る metrics。
|
||||
- stale / contradiction / renewal を作業中に resurfacing する導線。
|
||||
|
||||
したがって、次の Memory 設計は Codex / HermesAgent の単純なコピーではなく、以下を満たす必要がある。
|
||||
|
||||
```text
|
||||
external data / sessions / tickets / docs / code
|
||||
-> task-bound shoebox
|
||||
-> evidence file with provenance
|
||||
-> schema / representation
|
||||
-> hypotheses and disconfirmation
|
||||
-> Ticket / review / docs / implementation / decision product
|
||||
-> product impact and stale-feedback metrics
|
||||
```
|
||||
|
||||
この Objective では、以後の Memory 関連 Ticket / report / implementation をこの historical reference と sensemaking model の両方に照らして判断する。
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
title: "Pod: 任意ターンからの Fork(複数ターン巻き戻し)"
|
||||
state: 'closed'
|
||||
state: "planning"
|
||||
created_at: "2026-05-27T00:00:09Z"
|
||||
updated_at: '2026-06-20T16:31:29Z'
|
||||
updated_at: "2026-05-27T00:00:09Z"
|
||||
---
|
||||
|
||||
## Migration reference
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Closed as no longer needed. Arbitrary-turn Pod/session fork and multi-turn rewind are not part of the current desired workflow; current restore/rewind/fork behavior is sufficient for active use, and future history editing should be reopened as a narrower current-runtime design if needed.
|
||||
|
|
@ -4,22 +4,4 @@
|
|||
|
||||
Migrated from tickets/pod-session-fork.md. No legacy review file was present at migration time.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T16:31:28Z from: planning to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T16:31:29Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
Closed as no longer needed. Arbitrary-turn Pod/session fork and multi-turn rewind are not part of the current desired workflow; current restore/rewind/fork behavior is sufficient for active use, and future history editing should be reopened as a narrower current-runtime design if needed.
|
||||
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
title: "Prompt / Workflow 評価メトリクスと改善 Offer"
|
||||
state: 'closed'
|
||||
state: "planning"
|
||||
created_at: "2026-05-27T00:00:10Z"
|
||||
updated_at: '2026-06-20T16:31:29Z'
|
||||
updated_at: "2026-05-27T00:00:10Z"
|
||||
---
|
||||
|
||||
## Migration reference
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Closed as superseded/no longer needed. Workflow evaluation metrics and improvement planning have moved under the newer Memory redesign / team-workspace memory objectives, so this old prompt/workflow metrics ticket should not remain as a standalone planning item.
|
||||
|
|
@ -4,22 +4,4 @@
|
|||
|
||||
Migrated from tickets/prompt-eval-metrics.md. No legacy review file was present at migration time.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T16:31:29Z from: planning to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T16:31:29Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
Closed as superseded/no longer needed. Workflow evaluation metrics and improvement planning have moved under the newer Memory redesign / team-workspace memory objectives, so this old prompt/workflow metrics ticket should not remain as a standalone planning item.
|
||||
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
title: "TUI: navigation mode / block focus の設計"
|
||||
state: 'closed'
|
||||
state: "planning"
|
||||
created_at: "2026-05-27T00:00:15Z"
|
||||
updated_at: '2026-06-20T16:31:29Z'
|
||||
updated_at: "2026-05-27T00:00:15Z"
|
||||
---
|
||||
|
||||
## Migration reference
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Closed as no longer needed. The current TUI navigation/block-focus behavior is satisfactory, so the older navigation-mode design ticket is obsolete.
|
||||
|
|
@ -4,22 +4,4 @@
|
|||
|
||||
Migrated from tickets/tui-navigation-mode-design.md. No legacy review file was present at migration time.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T16:31:29Z from: planning to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T16:31:29Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
Closed as no longer needed. The current TUI navigation/block-focus behavior is satisfactory, so the older navigation-mode design ticket is obsolete.
|
||||
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
title: "Improve Pod notification injection guidance"
|
||||
state: 'closed'
|
||||
state: "planning"
|
||||
created_at: "2026-06-07T07:33:13Z"
|
||||
updated_at: '2026-06-20T16:23:37Z'
|
||||
updated_at: "2026-06-07T07:33:13Z"
|
||||
---
|
||||
|
||||
## Background
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Closed as stale/currently not needed. Orchestrator role/profile/workflow notification guidance has since improved, and the broad planning issue is not currently reproducing. If similar notification-as-user-turn confusion recurs in default profiles or generic notification wrappers, create a narrower ticket against the current prompt/profile state.
|
||||
|
|
@ -4,39 +4,4 @@
|
|||
|
||||
Created by LocalTicketBackend create.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: decision author: intake at: 2026-06-20T16:20:17Z -->
|
||||
|
||||
## Decision
|
||||
|
||||
ユーザー判断により、この Ticket は一旦 close 推奨とする。
|
||||
|
||||
理由:
|
||||
- Ticket 作成後に Orchestrator profile / role prompt / workflow guidance の改善が複数回入っている。
|
||||
- 現在の明示的な Orchestrator role では、通知を user request と誤認しているケースを最近見かけていない。
|
||||
- default profile では同種の誤認がまだ起き得る可能性はあるが、現時点でこの broad な planning Ticket を残しておくほどの実害・優先度は確認されていない。
|
||||
|
||||
判断:
|
||||
- この Ticket は stale / currently not needed として close してよい。
|
||||
- 将来 default profile や generic notify_wrapper で同じ問題が再発した場合は、現在の prompt/profile 状態を前提に、より狭い concrete Ticket として切り直す。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T16:23:37Z from: planning to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T16:23:37Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
Closed as stale/currently not needed. Orchestrator role/profile/workflow notification guidance has since improved, and the broad planning issue is not currently reproducing. If similar notification-as-user-turn confusion recurs in default profiles or generic notification wrappers, create a narrower ticket against the current prompt/profile state.
|
||||
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
{"id":"orch-plan-20260620-132846-1","ticket_id":"00001KVJHYP4Q","kind":"accepted_plan","accepted_plan":{"summary":"Plugin runtime を host-managed PluginInstanceRegistry 中心に再構成し、Tool/Service/Ingress を同一 Plugin instance の surface として扱う。既存 Tool-only component/raw wasm は compatibility adapter で維持し、新 instance component world / PDK / manifest/static inspection / plugin check-list-show reporting / lifecycle status / ingress test path / per-surface grants を実装する。","branch":"impl/00001KVJHYP4Q-plugin-instance-lifecycle","worktree":"/home/hare/Projects/yoi/.worktree/00001KVJHYP4Q-plugin-instance-lifecycle","role_plan":"Orchestrator は acceptance records を commit 後、専用 implementation worktree `.worktree/00001KVJHYP4Q-plugin-instance-lifecycle` を作成し、Coder をその child worktree への narrow write scope で起動する。Coder 実装後、Reviewer が instance registry boundary、legacy Tool compatibility、Service/Ingress grant validation、WIT/PDK/template/docs、ToolRegistry run-stability、no hidden context injection、diagnostics/bounds、Nix/package impact を重点確認する。"},"author":"yoi-orchestrator","at":"2026-06-20T13:28:46Z"}
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
---
|
||||
title: 'Plugin Service/Ingress component lifecycle surface'
|
||||
state: 'closed'
|
||||
created_at: '2026-06-20T13:01:37Z'
|
||||
updated_at: '2026-06-20T15:23:35Z'
|
||||
assignee: null
|
||||
queued_by: 'workspace-panel'
|
||||
queued_at: '2026-06-20T13:28:19Z'
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
現行の外部 Plugin 実装は Tool surface のみを提供しており、実行モデルも Tool call ごとの artifact 実行に寄っている。Discord Bridge や Minecraft Plugin 的な拡張では、Tool / Service / Ingress が同じ Plugin instance とその内部構造・状態・設定・diagnostics を共有できる必要がある。
|
||||
|
||||
この Ticket では、Plugin を「Tool call のたびに実行される wasm artifact」ではなく、Pod lifetime に紐づいて host が管理する **Plugin instance** として再設計し、一気に実装する。mutable state を持たない Plugin も意味論上は instance として扱い、per-call instantiate / pooling / lightweight execution は host runtime の最適化または legacy adapter の実装詳細に留める。
|
||||
|
||||
既存前提:
|
||||
|
||||
- `00001KT6Q08R9` で Feature contribution registry は Tool / Hook / BackgroundTask / Service provider descriptor 境界を導入済み。
|
||||
- 現行 Plugin package/runtime は Tool registration/execution まで実装済み。
|
||||
- `PluginSurface` には Tool / Service / Ingress などの概念があるが、実際の external wasm Plugin runtime は Tool only。
|
||||
- Plugin output は hidden context injection ではなく、Tool result、committed history、explicit notification/history path へ流す原則を維持する。
|
||||
|
||||
## Core model
|
||||
|
||||
- Plugin package は enabled/digest/grant 検証後、Pod 内に 1 個以上の host-managed `PluginInstance` として登録される。
|
||||
- Tool / Service / Ingress は Plugin instance が提供する surface であり、独立した実行主体ではない。
|
||||
- Tool call は model-visible ToolRegistry から thin dispatch handle を通り、同じ Plugin instance の command/query handler に入る。
|
||||
- Ingress event は host-managed external/internal event source から bounded typed event として入り、同じ Plugin instance の event handler に入る。
|
||||
- Service は Plugin instance lifecycle / background-owned capability / status/diagnostics を表す surface であり、Tool の代替ではない。
|
||||
- Wasm component は Plugin instance interface を実装する。Tool-only Plugin も instance interface に乗せる。
|
||||
- 既存の Tool-only component/raw wasm runtime は互換 adapter として維持し、長期意味論は instance-oriented に揃える。
|
||||
|
||||
概念図:
|
||||
|
||||
```text
|
||||
Enabled Plugin Package
|
||||
-> PluginInstanceRegistry
|
||||
-> PluginInstance
|
||||
-> lifecycle: start/status/stop
|
||||
-> tool dispatch: handle_tool(tool_name, input_json)
|
||||
-> ingress dispatch: handle_ingress(event_kind, event_json)
|
||||
-> diagnostics/status/actions via host-mediated paths
|
||||
|
||||
ToolRegistry entry
|
||||
-> PluginInstanceTool { plugin_ref, tool_name, registry }
|
||||
-> registry.call_tool(plugin_ref, tool_name, input)
|
||||
|
||||
Ingress source
|
||||
-> registry.deliver_ingress(plugin_ref, ingress_name, event)
|
||||
```
|
||||
|
||||
## Wasm / PDK interface direction
|
||||
|
||||
Define a new Component world/interface, e.g. `yoi:plugin/instance@1.0.0`, separate from the current Tool-only world. Exact WIT/schema names can change during implementation, but the implemented interface must represent the following lifecycle:
|
||||
|
||||
- `start(config_json) -> result/status`
|
||||
- `handle_tool(tool_name, input_json) -> tool_result_json`
|
||||
- `handle_ingress(ingress_name, event_json) -> action_batch_json/status`
|
||||
- `status() -> status_json`
|
||||
- `stop(reason_json) -> result/status`
|
||||
|
||||
PDK shape should be instance-oriented, for example:
|
||||
|
||||
```rust
|
||||
struct MyPlugin {
|
||||
// Mutable state is allowed but not required.
|
||||
}
|
||||
|
||||
impl yoi_plugin_pdk::Plugin for MyPlugin {
|
||||
fn start(ctx: StartContext) -> Result<Self, Error>;
|
||||
fn handle_tool(&mut self, ctx: ToolContext, tool: &str, input: JsonValue) -> Result<ToolOutput, Error>;
|
||||
fn handle_ingress(&mut self, ctx: IngressContext, ingress: &str, event: JsonValue) -> Result<ActionBatch, Error>;
|
||||
fn status(&self, ctx: StatusContext) -> Result<PluginStatus, Error>;
|
||||
fn stop(self, ctx: StopContext) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
yoi_plugin_pdk::export_plugin_instance!(MyPlugin);
|
||||
```
|
||||
|
||||
Tool-only helper macros may remain, but should become compatibility/convenience wrappers over the instance model where practical.
|
||||
|
||||
## Host runtime design
|
||||
|
||||
Add a `PluginInstanceRegistry` in the Pod feature/plugin layer.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- Build instance descriptors from discovered + enabled Plugin records.
|
||||
- Validate digest/version/source/grants before instance registration.
|
||||
- Start instances during Pod/Worker setup at the point where Tool registration can receive registry handles.
|
||||
- Register Tool surface entries as dispatch handles, not self-contained artifact executors.
|
||||
- Route Tool calls to `PluginInstance::handle_tool`.
|
||||
- Route Ingress events to `PluginInstance::handle_ingress`.
|
||||
- Provide status/diagnostics for `yoi plugin list/show` or equivalent inspection.
|
||||
- Stop instances on Pod shutdown/runtime teardown.
|
||||
- Keep model-visible Tool schemas run-stable for a Worker run; instance state must not mutate Tool schema mid-run.
|
||||
|
||||
Instance execution should be serialized per Plugin instance initially:
|
||||
|
||||
- Each instance owns its Wasm `Store`/component instance or legacy adapter state.
|
||||
- Calls enter through an actor/queue or equivalent single-flight guard.
|
||||
- Reentrancy is disallowed initially.
|
||||
- Concurrent Tool/Ingress calls to the same instance are queued or rejected with bounded diagnostics.
|
||||
- Timeout/trap/cancel marks the instance failed and either restarts according to policy or returns a bounded error.
|
||||
- Crash/restart/backoff/status are visible in diagnostics, not hidden in Tool prose.
|
||||
|
||||
## Runtime compatibility
|
||||
|
||||
Implement all active paths in one change set, but preserve compatibility:
|
||||
|
||||
- New instance world:
|
||||
- `world = "yoi:plugin/instance@1.0.0"` or equivalent.
|
||||
- Host keeps component instance alive across calls.
|
||||
- Tool/Ingress/Service dispatch share that instance.
|
||||
- Existing Component Tool world:
|
||||
- Continue supporting `world = "yoi:plugin/tool@1.0.0"`.
|
||||
- Wrap as a legacy `PluginInstance` adapter.
|
||||
- Adapter may still instantiate per call internally, but ToolRegistry sees an instance dispatch target.
|
||||
- Existing raw core-Wasm Tool runtime:
|
||||
- Continue supporting current `kind = "wasm"` / `abi = "yoi-plugin-wasm-1"` as a legacy Tool adapter.
|
||||
- No Service/Ingress support for raw ABI unless explicitly added later.
|
||||
|
||||
This avoids breaking existing Tool Plugin packages while moving Pod/plugin architecture to the instance model immediately.
|
||||
|
||||
## Manifest / package model
|
||||
|
||||
Extend manifest/static inspection so a package can declare instance-backed surfaces:
|
||||
|
||||
- `surfaces = ["tool"]` remains valid.
|
||||
- `surfaces = ["tool", "service", "ingress"]` becomes valid when runtime/interface supports it.
|
||||
- `[[tools]]` remains the model-visible Tool schema declaration source.
|
||||
- Add Service declarations with id/name/description/lifecycle/required host APIs/side effects/status metadata.
|
||||
- Add Ingress declarations with id/name/event kinds/input schema/source expectations/side effects/action outputs.
|
||||
- Static validation must reject Service/Ingress declarations for runtimes/interfaces that cannot implement them.
|
||||
- `yoi plugin check/list/show` must report Tool/Service/Ingress eligibility, grants, rejected surfaces, runtime compatibility, diagnostics, and whether a package is legacy Tool-only or instance-capable.
|
||||
|
||||
Permission/grant model:
|
||||
|
||||
- Surface grants stay explicit: `surface=tool`, `surface=service`, `surface=ingress`.
|
||||
- Tool grants remain per Tool name.
|
||||
- Service grants are per Service id/name.
|
||||
- Ingress grants are per Ingress id/name and event source/kind where applicable.
|
||||
- Host APIs (`https`, `fs`, future secrets/network/event sources) remain separately grant-gated.
|
||||
- Output actions from Ingress/Service must be separately constrained; Plugin instance access does not imply arbitrary Yoi mutation authority.
|
||||
- SDK/PDK helper availability is never authority.
|
||||
|
||||
## Tool / Service / Ingress interaction
|
||||
|
||||
- Tool processing must be able to access the same Plugin instance used by Service/Ingress.
|
||||
- Tool execution remains model/user initiated and returns through the ordinary Tool result/history path.
|
||||
- Service/Ingress must not secretly call model Tools or mutate context/history directly.
|
||||
- Service/Ingress may return host-mediated actions; host applies only approved actions to explicit durable/visible paths.
|
||||
- Long-running work started from a Tool should return an accepted/status result and continue through instance/service diagnostics or explicit action paths rather than blocking the Tool indefinitely.
|
||||
|
||||
## Ingress and action paths
|
||||
|
||||
Ingress is the boundary for external events entering Yoi.
|
||||
|
||||
- Events are untrusted, bounded, typed inputs.
|
||||
- Event delivery must not insert hidden context.
|
||||
- Allowed outputs must be explicit action variants, initially limited to safe host-mediated paths such as diagnostics/status and, if implemented, Notify/SystemItem/Companion-visible messages through existing durable mechanisms.
|
||||
- Arbitrary UI channels are prohibited.
|
||||
- If an action path does not yet have a safe host API, the implementation should expose diagnostics/status rather than inventing an unsafe path.
|
||||
|
||||
## Implementation scope
|
||||
|
||||
This Ticket is no longer design-only. Implement the complete instance-oriented plugin runtime boundary in one integrated change:
|
||||
|
||||
1. Instance model and registry in Pod plugin feature code.
|
||||
2. ToolRegistry dispatch through Plugin instance handles.
|
||||
3. New Component instance world/resource files and Rust PDK support.
|
||||
4. Legacy Tool component/raw wasm adapters behind the instance registry.
|
||||
5. Manifest/static validation additions for Service/Ingress declarations.
|
||||
6. `yoi plugin check/list/show` reporting updates.
|
||||
7. Host-managed start/status/stop lifecycle and bounded diagnostics.
|
||||
8. Ingress dispatch API and at least one test/in-process ingress delivery path.
|
||||
9. Grant checks for Tool/Service/Ingress surfaces and host APIs.
|
||||
10. Docs/templates updated so Tool-only vs instance-capable Plugin authoring is explicit.
|
||||
|
||||
Discord Bridge itself remains out of scope, but the implemented runtime must be sufficient for a Discord Bridge Plugin to be designed on top of it without another foundational runtime redesign.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Implementing Discord Bridge itself.
|
||||
- Granting raw ambient WASI network/socket access to Plugin code.
|
||||
- Public registry/install/update/signature tooling.
|
||||
- Arbitrary Plugin UI channel.
|
||||
- Hidden prompt/context injection.
|
||||
- Allowing Service/Ingress to mutate model-visible Tool schema mid-run.
|
||||
|
||||
## 受け入れ条件
|
||||
|
||||
- Pod/plugin architecture uses a `PluginInstanceRegistry` or equivalent host-managed instance boundary.
|
||||
- Existing Tool Plugin packages still work through the instance registry compatibility path.
|
||||
- A new instance-capable Component Plugin can expose at least one Tool and have multiple Tool calls reach the same Plugin instance during a Pod run.
|
||||
- A new instance-capable Component Plugin can expose at least one Ingress handler and receive a bounded in-process test event through the registry.
|
||||
- Service lifecycle/status is represented and visible in inspection/diagnostics, even if the first implementation only supports host-managed start/status/stop and no real external socket source.
|
||||
- Tool / Service / Ingress grants are validated independently; sharing a Plugin instance does not bypass per-surface authorization.
|
||||
- Tool schemas remain run-stable and model-visible only through normal ToolRegistry construction.
|
||||
- Plugin output/event effects use Tool results or explicit durable/visible host-mediated paths; no hidden context injection is introduced.
|
||||
- `yoi plugin check/list/show` distinguishes legacy Tool-only packages from instance-capable packages and explains rejected Service/Ingress surfaces.
|
||||
- Rust PDK/template/docs include instance-oriented authoring in addition to legacy/convenience Tool-only authoring.
|
||||
- Focused tests cover:
|
||||
- manifest validation for Tool/Service/Ingress declarations;
|
||||
- legacy Tool component execution through the instance registry;
|
||||
- instance state persistence across two Tool calls;
|
||||
- Tool call dispatch to the same instance used by Ingress delivery;
|
||||
- grant denial for missing Service/Ingress grants;
|
||||
- timeout/trap/failure diagnostics for an instance call.
|
||||
- Validation before completion includes `cargo fmt --check`, relevant `cargo test`, `cargo check`, `git diff --check`, `yoi ticket doctor`, and `nix build .#yoi --no-link`.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
Plugin Service/Ingress component lifecycle surface を実装し、Orchestrator worktree の `orchestration` branch に統合した。
|
||||
|
||||
主な成果:
|
||||
- Pod plugin feature に host-managed `PluginInstanceRegistry` / instance handle 境界を追加し、Tool dispatch を instance 経由に変更。
|
||||
- New instance-capable component world `yoi:plugin/instance@1.0.0` と WIT resource を追加。
|
||||
- `yoi-plugin-pdk` と Rust component template に instance-oriented authoring support を追加。
|
||||
- Existing component Tool world / raw wasm Tool runtime を instance registry compatibility path に維持。
|
||||
- Manifest/static validation に Service / Ingress declarations と per-surface grant validation を追加。
|
||||
- Service lifecycle/status/diagnostics と bounded in-process ingress dispatch path を実装。
|
||||
- Tool / Service / Ingress enabled-surface filtering を runtime install, dispatch guard, and resolved static inspection / `yoi plugin list/show` に適用。
|
||||
- `plugin check` は package declaration inspection、resolved `plugin list/show` は selected/enabled surfaces に基づく reporting に分離。
|
||||
- Focused tests added for manifest validation, legacy Tool compatibility, instance state persistence, ingress dispatch, Service/Ingress grant denial, failure diagnostics, and partial enabled-surface static reporting。
|
||||
|
||||
統合・検証:
|
||||
- Merge commit: `43c9216e merge: plugin instance lifecycle surface`
|
||||
- Implementation commits: `147a6005`, `870bcc76`, `79ca0f7f`, `627c8f36`
|
||||
- Reviewer final verdict: approve
|
||||
- Validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p manifest plugin -- --nocapture`, `cargo test -p pod plugin -- --nocapture`, `cargo test -p yoi plugin -- --nocapture`, `cargo check -p yoi`, `cargo check -p yoi-plugin-pdk`, template cargo-check, `yoi ticket doctor`, and `nix build .#yoi --no-link`。
|
||||
|
||||
範囲外:
|
||||
- Discord Bridge 本体、public registry/install/update/signature tooling、arbitrary Plugin UI channel、hidden context injection、Service/Ingress による model-visible Tool schema mutation は実装していない。
|
||||
|
|
@ -1,705 +0,0 @@
|
|||
<!-- event: create author: "yoi ticket" at: 2026-06-20T13:01:37Z -->
|
||||
|
||||
## 作成
|
||||
|
||||
LocalTicketBackend によって作成されました。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: intake_summary author: hare at: 2026-06-20T13:02:36Z -->
|
||||
|
||||
## Intake summary
|
||||
|
||||
Marked ready by `yoi ticket state`.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: "yoi ticket" at: 2026-06-20T13:02:36Z from: planning to: ready reason: cli_state field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Marked ready by `yoi ticket state`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: workspace-panel at: 2026-06-20T13:28:19Z from: ready to: queued reason: queued field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を `workspace-panel` が queued にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: decision author: yoi-orchestrator at: 2026-06-20T13:29:10Z -->
|
||||
|
||||
## Decision
|
||||
|
||||
Routing decision: implementation_ready
|
||||
|
||||
Reason:
|
||||
- Workspace Dashboard Queue により、この Ticket は Orchestrator routing 対象として明示許可された。
|
||||
- Ticket body は Plugin instance model / registry、new Component instance world、legacy Tool adapters、manifest/static validation、plugin check/list/show reporting、Service/Ingress lifecycle/status、Ingress test path、per-surface grants、docs/templates/PDK updates、validation を詳細に定義している。
|
||||
- 未解決 relation blocker はない。
|
||||
- 現在 queued はこの Ticket のみ、inprogress は 0 件、spawned child implementation Pods はなし、matching branch/worktree はなし、Orchestrator worktree は clean。
|
||||
- Risk domain は plugin / wasm-component / service / ingress / lifecycle / grants / runtime architecture だが、Ticket は no hidden context injection、ToolRegistry run-stability、legacy Tool compatibility、no ambient WASI network/socket、per-surface grants、host-mediated outputs を明示している。bounded context check 後も implementation 前に必要な追加 human decision は見つからなかった。
|
||||
|
||||
Evidence checked:
|
||||
- Ticket `00001KVJHYP4Q` body / thread / relations / artifacts。
|
||||
- `TicketRelationQuery(00001KVJHYP4Q)`: no blockers。
|
||||
- `TicketOrchestrationPlanQuery(00001KVJHYP4Q)`: no previous plan records; accepted plan recorded now。
|
||||
- Workspace state:
|
||||
- Orchestrator worktree clean at `7f06e656`。
|
||||
- queued: this Ticket only。
|
||||
- inprogress: 0。
|
||||
- visible Pods are self/peers only; spawned children 0。
|
||||
- no matching implementation branch/worktree。
|
||||
|
||||
IntentPacket:
|
||||
|
||||
Intent:
|
||||
- Move Plugin runtime semantics from per-Tool artifact execution to host-managed `PluginInstance` / `PluginInstanceRegistry`。
|
||||
- Treat Tool / Service / Ingress as surfaces of the same Plugin instance, sharing instance state/config/diagnostics while preserving explicit authorization and ordinary visible output paths。
|
||||
- Preserve existing Tool-only component/raw wasm Plugin packages through compatibility adapters。
|
||||
|
||||
Binding decisions / invariants:
|
||||
- Existing Tool Plugin packages must continue to work through instance registry compatibility path。
|
||||
- Tool execution remains model/user initiated and returns through ordinary Tool result/history path。
|
||||
- Service/Ingress must not secretly call model Tools or mutate context/history directly。
|
||||
- Plugin outputs/events must use Tool results or explicit durable/visible host-mediated paths; no hidden context injection。
|
||||
- Tool schemas remain run-stable and model-visible only through normal ToolRegistry construction。
|
||||
- Per-surface grants are independent: Tool, Service, Ingress grants must be validated separately; sharing an instance must not bypass authorization。
|
||||
- Host APIs remain separately grant-gated。
|
||||
- No raw ambient WASI network/socket authority。
|
||||
- Ingress events are bounded typed untrusted inputs。
|
||||
- If a safe host action path does not exist, expose diagnostics/status rather than inventing unsafe paths。
|
||||
|
||||
Requirements / acceptance criteria:
|
||||
- Add `PluginInstanceRegistry` or equivalent host-managed instance boundary。
|
||||
- ToolRegistry dispatch goes through Plugin instance handles。
|
||||
- Add new Component instance world/resource files and Rust PDK support。
|
||||
- Add legacy Tool component/raw wasm adapters behind the instance registry。
|
||||
- Extend manifest/static validation for Service/Ingress declarations and runtime compatibility。
|
||||
- Update `yoi plugin check/list/show` reporting for legacy Tool-only vs instance-capable packages and rejected surfaces。
|
||||
- Add host-managed start/status/stop lifecycle and bounded diagnostics。
|
||||
- Add Ingress dispatch API and at least one bounded in-process ingress delivery test path。
|
||||
- Validate Tool/Service/Ingress grants independently。
|
||||
- Update docs/templates for instance-oriented authoring。
|
||||
- Focused tests cover manifest validation, legacy compatibility, instance state persistence across Tool calls, Tool/Ingress shared instance dispatch, grant denial, timeout/trap/failure diagnostics。
|
||||
- Validation includes `cargo fmt --check`, relevant tests/checks, `git diff --check`, `yoi ticket doctor`, and `nix build .#yoi --no-link`。
|
||||
|
||||
Escalate if:
|
||||
- The instance boundary cannot be implemented without broad Worker/ToolRegistry redesign beyond Ticket scope。
|
||||
- Preserving legacy Tool runtime while adding instance runtime would substantially distort architecture。
|
||||
- Safe Service/Ingress host action semantics require a product decision not already specified。
|
||||
- WIT/PDK interface shape requires a compatibility-breaking public API decision beyond this Ticket。
|
||||
|
||||
Next action:
|
||||
- Record `queued -> inprogress` and commit Ticket records before creating implementation worktree and spawning Coder。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-20T13:29:23Z from: queued to: inprogress reason: orchestrator_acceptance_plugin_instance_lifecycle field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket body/thread, relation metadata, Orchestrator worktree state, visible Pods, existing branch/worktree, and bounded Plugin instance lifecycle context were checked. There is no unresolved blocking dependency, no inprogress/capacity blocker, and no missing planning decision. Accepting this queued Ticket for implementation before worktree/Pod side effects.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T13:30:38Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Implementation orchestration start:
|
||||
- Acceptance records were committed on Orchestrator branch: `5ec8bae9 ticket: accept plugin instance lifecycle`。
|
||||
- Created implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVJHYP4Q-plugin-instance-lifecycle` on branch `impl/00001KVJHYP4Q-plugin-instance-lifecycle` at `5ec8bae9`。
|
||||
- Spawned Coder Pod `yoi-coder-00001KVJHYP4Q` with write scope limited to the implementation worktree and read-only root scope only for runtime workspace identity。
|
||||
- Coder task explicitly forbids root/original workspace implementation, unsafe hidden context/action paths, broad ToolRegistry bypasses, merge, Ticket close, and cleanup。
|
||||
|
||||
Next action:
|
||||
- Wait for Coder implementation report, then inspect branch diff/validation evidence and route to Reviewer。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T14:16:45Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Coder implementation report received from `yoi-coder-00001KVJHYP4Q`.
|
||||
|
||||
Implementation commit:
|
||||
- `147a6005 plugin: add instance lifecycle surface`
|
||||
|
||||
Changed areas reported:
|
||||
- Manifest support for Service/Ingress declarations and permissions。
|
||||
- New `yoi:plugin/instance@1.0.0` WIT/resource surface。
|
||||
- Host-managed `PluginInstanceRegistry` / `PluginInstanceHandle` in Pod plugin feature layer。
|
||||
- Plugin Tool registration/dispatch now routes through instance handles。
|
||||
- Legacy raw-wasm and `yoi:plugin/tool@1.0.0` component Tool runtimes preserved through registry adapter path。
|
||||
- Instance lifecycle/status/stop/diagnostics surfaces and bounded Ingress dispatch API added。
|
||||
- Independent Tool/Service/Ingress grant validation added。
|
||||
- Static inspection / Plugin CLI diagnostics extended for Service/Ingress eligibility。
|
||||
- Rust PDK instance-oriented types/trait/macro support added。
|
||||
- Rust component instance template resources added。
|
||||
- Plugin component model docs updated。
|
||||
- Tests added/updated for manifest validation, CLI reporting/templates, legacy compatibility, grant checks, and in-process shared Tool/Ingress dispatch。
|
||||
|
||||
Coder validation reported as passing:
|
||||
- `cargo test -p manifest plugin -- --nocapture`
|
||||
- `cargo test -p pod plugin -- --nocapture`
|
||||
- `cargo test -p yoi plugin -- --nocapture`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo check -p yoi-plugin-pdk`
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check`
|
||||
- `yoi ticket doctor`
|
||||
- `nix build .#yoi --no-link`
|
||||
|
||||
Dependency/package impact:
|
||||
- No new Rust dependencies。
|
||||
- Added package/resource files under `resources/plugin/templates/rust-component-instance/` and `resources/plugin/wit/yoi-plugin-instance-v1.wit`。
|
||||
- No `Cargo.lock` or `package.nix` changes required; Nix build succeeded。
|
||||
|
||||
Known risks / deferrals:
|
||||
- Instance Component ABI is intentionally minimal string-JSON host dispatch (`start`, `handle-tool`, `handle-ingress`, `status`, `stop`) and should be reviewed before treating it as stable public low-level ABI。
|
||||
- Service lifecycle is surfaced as host-managed registration/status/stop diagnostics; no unsafe hidden model/tool/context action path was added。
|
||||
- Rust PDK instance macro provides author-facing instance helpers, but full cargo-component generated glue ergonomics may need follow-up polish if public PDK shape becomes stricter。
|
||||
|
||||
Orchestrator evidence checked before review dispatch:
|
||||
- Implementation worktree is clean。
|
||||
- HEAD is `147a6005`。
|
||||
- Diff from acceptance `5ec8bae9..HEAD` is one implementation commit touching 12 files, about 1399 insertions / 34 deletions。
|
||||
- `git diff --check 5ec8bae9..HEAD` produced no diagnostics。
|
||||
|
||||
Next action:
|
||||
- Dispatch Reviewer for r1 review against Ticket requirements, with focus on instance registry boundary, legacy compatibility, ToolRegistry run-stability, no hidden context/action paths, per-surface grants, Service/Ingress lifecycle semantics, static inspection/CLI reporting, WIT/PDK/template correctness, bounds/resource limits, and validation evidence。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: plan author: yoi-orchestrator at: 2026-06-20T14:17:31Z -->
|
||||
|
||||
## Plan
|
||||
|
||||
Review dispatch:
|
||||
- Spawned Reviewer Pod `yoi-reviewer-00001KVJHYP4Q-r1` against implementation branch `impl/00001KVJHYP4Q-plugin-instance-lifecycle`。
|
||||
- Review target commit: `147a6005 plugin: add instance lifecycle surface`。
|
||||
- Review baseline: `5ec8bae9`。
|
||||
- Reviewer task focuses on instance registry boundary, legacy Tool compatibility, ordinary Tool result/history path, Service/Ingress no hidden action/context paths, ToolRegistry run-stability, per-surface and host API grant independence, no ambient WASI/network authority, bounded ingress/events/diagnostics, static inspection no-exec behavior, WIT/PDK/template coherence, tests, and Nix/package evidence。
|
||||
- Reviewer is instructed not to edit source, commit, merge, close the Ticket, or use TicketReview directly; it will report verdict/evidence back to Orchestrator。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: review author: yoi-reviewer-00001KVJHYP4Q-r1 at: 2026-06-20T14:24:40Z status: request_changes -->
|
||||
|
||||
## Review: request changes
|
||||
|
||||
Verdict: `request_changes`
|
||||
|
||||
確認範囲:
|
||||
- Ticket contract / Orchestrator IntentPacket。
|
||||
- Implementation diff: `5ec8bae9..147a6005`。
|
||||
- 主な対象:
|
||||
- `crates/pod/src/feature/plugin.rs`
|
||||
- `crates/manifest/src/plugin.rs`
|
||||
- `crates/yoi/src/plugin_cli.rs`
|
||||
- `crates/plugin-pdk/src/lib.rs`
|
||||
- `resources/plugin/wit/yoi-plugin-instance-v1.wit`
|
||||
- `resources/plugin/templates/rust-component-instance/*`
|
||||
- Plugin component docs/templates and focused tests。
|
||||
- Hidden history/context injection、ToolRegistry bypass、Ingress/Service production call path、ambient WASI/network authority の有無を確認。
|
||||
|
||||
Blocking issues:
|
||||
|
||||
1. Rust instance PDK/template が buildable ではなく、WIT component instance ABI を実際に export していない。
|
||||
- `resources/plugin/templates/rust-component-instance/Cargo.toml` は repository 配下 package だが `[workspace]` がなく、direct authoring check が workspace membership error で失敗する。
|
||||
- `resources/plugin/templates/rust-component-instance/src/lib.rs` は `ToolOutput::text(...)` を呼ぶが、`crates/plugin-pdk/src/lib.rs` には `ToolOutput::new`, `ToolOutput::json`, `ToolOutput::summary` しかない。
|
||||
- `export_plugin_instance!` は generated WIT bindings / generated `export!` macro for `world instance` を実装していない。raw placeholder `#[unsafe(export_name = "start")]` と private Rust methods を定義するだけで、host が期待する component-model exports (`start`, `handle-tool`, `handle-ingress`, `status`, `stop`) を生成しない。
|
||||
- Ticket が要求する WIT/PDK/template coherence と instance-oriented authoring surface を満たしていない。
|
||||
|
||||
2. Component instance lifecycle が status/error outputs を parse せず、component `status` export が実質 unused。
|
||||
- `PluginInstance::status` は host-side lifecycle/diagnostics のみを返し、component runtime の `status` export を呼ばない。
|
||||
- `PluginComponentInstanceRuntime::start` は component `start` export の returned string を捨てている。
|
||||
- `PluginComponentInstanceRuntime::stop` も returned string を捨てている。
|
||||
- WIT は `status` を export しているが、host-side runtime method がない。
|
||||
- Component が `{"error": ...}` を `start` から返しても host が started と扱い得るため、lifecycle/status/diagnostics acceptance criteria を満たしていない。
|
||||
|
||||
3. Service/Ingress surfaces が Tool 登録なしでは host-managed Plugin instance を作成・保持しない。
|
||||
- `PluginToolFeature::install` の実 install path では Service declarations は metadata only、Ingress declarations は authorization-check only。
|
||||
- Instance は Tool registration loop 内でのみ作られる。
|
||||
- Service-only / Ingress-only instance-capable plugin は host-managed instance、lifecycle start、status、diagnostics、retained ingress dispatch handle を得られない。
|
||||
- `deliver_ingress` は production caller に接続されておらず、focused ingress test は synthetic `TestIngress` instance を使っている。
|
||||
- Ticket intent の “Tool / Service / Ingress as surfaces of the same Plugin instance” を満たしていない。
|
||||
|
||||
Non-blocking concerns / follow-ups:
|
||||
- Human `yoi plugin show` output でも Service/Ingress eligibility を明示すると JSON なしでも rejected surfaces が分かりやすい。
|
||||
- Real instance-component tests が弱い。Rust/PDK generated `yoi:plugin/instance@1.0.0` component の build/execution through start/tool/ingress/status/stop coverage が欲しい。
|
||||
|
||||
Reviewer validation:
|
||||
- `git diff --check 5ec8bae9..HEAD`: passed。
|
||||
- `cargo fmt --check`: passed。
|
||||
- `cargo test -p manifest plugin -- --nocapture`: passed。
|
||||
- `cargo test -p pod plugin -- --nocapture`: passed。
|
||||
- `cargo test -p yoi plugin -- --nocapture`: passed。
|
||||
- `cargo check -p yoi`: passed。
|
||||
- `cargo check -p yoi-plugin-pdk`: passed。
|
||||
- `yoi ticket doctor`: passed。
|
||||
- `nix build .#yoi --no-link`: passed。
|
||||
- Additional focused template validation failed: `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml` with workspace membership error。
|
||||
|
||||
Worktree status at review end: clean。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T14:43:43Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Coder r1-fix report received from `yoi-coder-00001KVJHYP4Q`.
|
||||
|
||||
New fix commit:
|
||||
- `870bcc76 plugin: fix instance lifecycle blockers`
|
||||
|
||||
Branch commits now:
|
||||
- `870bcc76 plugin: fix instance lifecycle blockers`
|
||||
- `147a6005 plugin: add instance lifecycle surface`
|
||||
|
||||
Fix summary:
|
||||
- `crates/plugin-pdk/src/lib.rs`:
|
||||
- Reworked instance PDK export macro to use generated `wit-bindgen` `Guest` / `export!` shape instead of raw placeholder exports。
|
||||
- Added coherent instance-world helpers/status/error JSON handling。
|
||||
- `resources/plugin/templates/rust-component-instance/`:
|
||||
- Added empty `[workspace]` so template checks as an in-tree standalone authoring package。
|
||||
- Fixed template API mismatch by using existing `ToolOutput::json`。
|
||||
- Added `wit_bindgen::generate!` for `instance` world and updated export macro usage。
|
||||
- `crates/pod/src/feature/plugin.rs`:
|
||||
- Component instance lifecycle now decodes `start` / `stop` returned JSON and fails closed on returned errors / failed status。
|
||||
- Host now calls component `status` export and includes component status in `PluginInstanceStatus`。
|
||||
- Service/Ingress-capable installs create and retain host-managed instances even without Tool registration。
|
||||
- Added retained registry handle and bounded installed-path Ingress dispatch API。
|
||||
- Added tests for component start error handling, component status/stop reporting, service-only instance retention, and installed-path Tool/Ingress shared-instance dispatch。
|
||||
- No `Cargo.lock`, dependency, or `package.nix` changes。
|
||||
|
||||
Coder validation reported as passing:
|
||||
- `cargo test -p manifest plugin -- --nocapture`
|
||||
- `cargo test -p pod plugin -- --nocapture`
|
||||
- `cargo test -p yoi plugin -- --nocapture`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo check -p yoi-plugin-pdk`
|
||||
- `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml`
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check 5ec8bae9..HEAD`
|
||||
- `yoi ticket doctor`
|
||||
- `nix build .#yoi --no-link`
|
||||
|
||||
Remaining deferral / risk:
|
||||
- Instance PDK now uses `wit-bindgen` export glue and template checks, but `yoi:plugin/instance@1.0.0` remains first public shape and should be reviewed before broad external stability promises。
|
||||
|
||||
Orchestrator evidence checked before r2 review:
|
||||
- Implementation worktree is clean。
|
||||
- HEAD is `870bcc76`。
|
||||
- Fix diff from `147a6005..HEAD` touches 4 files, about 506 insertions / 119 deletions。
|
||||
- Overall `git diff --check 5ec8bae9..HEAD` produced no diagnostics。
|
||||
|
||||
Next action:
|
||||
- Route back to Reviewer for r2 verification of the PDK/template export glue, lifecycle status/error parsing, Service/Ingress instance retention, installed-path ingress dispatch, and final acceptance criteria。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: review author: yoi-reviewer-00001KVJHYP4Q-r1 at: 2026-06-20T14:50:21Z status: request_changes -->
|
||||
|
||||
## Review: request changes
|
||||
|
||||
Verdict: `request_changes`
|
||||
|
||||
確認範囲:
|
||||
- Ticket authority / Orchestrator IntentPacket。
|
||||
- Fix diff: `147a6005..870bcc76`。
|
||||
- Overall implementation shape: `5ec8bae9..870bcc76`。
|
||||
- 主な対象:
|
||||
- `crates/plugin-pdk/src/lib.rs`
|
||||
- `resources/plugin/templates/rust-component-instance/Cargo.toml`
|
||||
- `resources/plugin/templates/rust-component-instance/src/lib.rs`
|
||||
- `resources/plugin/wit/yoi-plugin-instance-v1.wit`
|
||||
- `crates/pod/src/feature/plugin.rs`
|
||||
- `crates/manifest/src/plugin.rs`
|
||||
- `crates/yoi/src/plugin_cli.rs`
|
||||
|
||||
Previous blocker verification:
|
||||
- Rust instance PDK/template buildability / WIT export shape: substantially addressed。
|
||||
- `export_plugin_instance!` now targets generated `wit-bindgen` `Guest` / `export!` shape。
|
||||
- Instance template has empty `[workspace]`, uses `ToolOutput::json`, and includes `wit_bindgen::generate!`。
|
||||
- Focused template validation passes。
|
||||
- Component lifecycle start/status/stop handling: substantially addressed。
|
||||
- Host parses lifecycle JSON and fails closed on error/failure status。
|
||||
- Component `status` export is called and reflected in instance status。
|
||||
- Stop output is decoded/reported。
|
||||
- Service/Ingress instance retention and installed-path dispatch: substantially addressed。
|
||||
- `PluginToolFeature` retains a `PluginInstanceRegistry`。
|
||||
- Service/Ingress-capable installs can create/retain instance without Tool registration。
|
||||
- Installed-path `dispatch_ingress` exists and tests cover shared Tool/Ingress dispatch。
|
||||
|
||||
Blocking issue:
|
||||
|
||||
1. Enabled surface selection is not enforced per surface during descriptor/install, so mixed-surface plugins can expose or block the wrong surfaces。
|
||||
- `PluginToolFeature` is gated at coarse whole-record level by whether any of Tool/Service/Ingress appears in `record.enabled_surfaces`。
|
||||
- Once feature exists, descriptor and install paths iterate over all declared manifest services/tools/ingresses, not only selected/enabled surfaces。
|
||||
- Concrete risk:
|
||||
- Plugin declaring both Tool and Service with only Service selected can still attempt to authorize/register Tool。
|
||||
- Missing Tool grant can fail selected Service install。
|
||||
- If Tool grant exists, unselected Tool can become model-visible。
|
||||
- Unselected Service/Ingress can be provided/started if their grants exist。
|
||||
- This violates the Ticket requirement that Tool / Service / Ingress grants and exposure remain independent and explicitly gated。
|
||||
|
||||
Required fix:
|
||||
- Filter descriptor/install loops by `record.enabled_surfaces`。
|
||||
- Ensure registered/denial behavior accounts for selected surface set, not all manifest declarations。
|
||||
- Add focused tests for mixed-surface packages with partial enabled surfaces:
|
||||
- Service selected, Tool declared but not selected: Service installs and Tool does not register。
|
||||
- Tool selected, Service/Ingress declared but not selected: no Service/Ingress instance/dispatch exposure。
|
||||
- Missing grants for unselected surfaces do not fail selected-surface install。
|
||||
- Grants for unselected surfaces do not cause exposure。
|
||||
|
||||
Non-blocking concern:
|
||||
- Existing disabled-surface test only clears all enabled surfaces and does not cover mixed-surface partial enablement。
|
||||
|
||||
Reviewer validation:
|
||||
- `cargo fmt --check`: passed。
|
||||
- `git diff --check 5ec8bae9..HEAD`: passed。
|
||||
- `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml`: passed。
|
||||
- `cargo test -p manifest plugin -- --nocapture`: passed。
|
||||
- `cargo test -p pod plugin -- --nocapture`: passed。
|
||||
- `cargo test -p yoi plugin -- --nocapture`: passed。
|
||||
- `cargo check -p yoi`: passed。
|
||||
- `cargo check -p yoi-plugin-pdk`: passed。
|
||||
- `yoi ticket doctor`: passed。
|
||||
- `nix build .#yoi --no-link`: passed。
|
||||
|
||||
Worktree status at end: clean。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T14:59:25Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Coder r2-fix report received from `yoi-coder-00001KVJHYP4Q`.
|
||||
|
||||
New fix commit:
|
||||
- `79ca0f7f plugin: enforce enabled lifecycle surfaces`
|
||||
|
||||
Branch commits now:
|
||||
- `79ca0f7f plugin: enforce enabled lifecycle surfaces`
|
||||
- `870bcc76 plugin: fix instance lifecycle blockers`
|
||||
- `147a6005 plugin: add instance lifecycle surface`
|
||||
|
||||
Fix summary:
|
||||
- `crates/pod/src/feature/plugin.rs`:
|
||||
- Added per-surface `record.enabled_surfaces` gating for Plugin descriptor construction。
|
||||
- Filtered install loops so Tool / Service / Ingress setup only processes selected surfaces。
|
||||
- Changed install denial/exposure accounting to consider selected surfaces only。
|
||||
- Added runtime dispatch guards so unselected Tool/Ingress surfaces cannot be used even if grants exist。
|
||||
- Added focused tests for mixed-surface partial enablement:
|
||||
- Service selected, Tool declared but not selected: Service installs, Tool does not register, missing Tool grants do not fail install。
|
||||
- Tool selected, Service/Ingress declared but not selected: no Service descriptor/install exposure, Ingress dispatch denied even with grants。
|
||||
- Kept previous lifecycle/status/ingress retention tests passing。
|
||||
|
||||
Coder validation reported as passing:
|
||||
- `cargo test -p manifest plugin -- --nocapture`
|
||||
- `cargo test -p pod plugin -- --nocapture`
|
||||
- `cargo test -p yoi plugin -- --nocapture`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo check -p yoi-plugin-pdk`
|
||||
- `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml`
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check 5ec8bae9..HEAD`
|
||||
- `yoi ticket doctor`
|
||||
- `nix build .#yoi --no-link`
|
||||
|
||||
Orchestrator evidence checked before r3 review:
|
||||
- Implementation worktree is clean。
|
||||
- HEAD is `79ca0f7f`。
|
||||
- Fix diff from `870bcc76..HEAD` touches only `crates/pod/src/feature/plugin.rs`, about 190 insertions / 97 deletions。
|
||||
- Overall `git diff --check 5ec8bae9..HEAD` produced no diagnostics。
|
||||
|
||||
Next action:
|
||||
- Route back to Reviewer for r3 verification of per-surface enabled selection enforcement and final acceptance criteria。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: review author: yoi-reviewer-00001KVJHYP4Q-r1 at: 2026-06-20T15:04:08Z status: request_changes -->
|
||||
|
||||
## Review: request changes
|
||||
|
||||
Verdict: `request_changes`
|
||||
|
||||
確認範囲:
|
||||
- Ticket authority / Orchestrator IntentPacket。
|
||||
- r3 fix diff: `870bcc76..79ca0f7f`。
|
||||
- Relevant overall implementation shape: `5ec8bae9..79ca0f7f`。
|
||||
- 主な対象:
|
||||
- `crates/pod/src/feature/plugin.rs`
|
||||
- `crates/yoi/src/plugin_cli.rs`
|
||||
- `crates/manifest/src/plugin.rs`
|
||||
- `crates/plugin-pdk/src/lib.rs`
|
||||
- `resources/plugin/templates/rust-component-instance/*`
|
||||
- `resources/plugin/wit/yoi-plugin-instance-v1.wit`
|
||||
|
||||
Previous r2 blocker verification:
|
||||
- Runtime/registration side is mostly fixed。
|
||||
- Descriptor construction is gated by `record.enabled_surfaces`。
|
||||
- Install loops now gate Service / Ingress / Tool setup independently。
|
||||
- Install denial accounting counts selected exposed surfaces rather than only Tools。
|
||||
- Runtime dispatch guards exist for unselected Tool/Ingress use。
|
||||
- Tests cover requested selected/unselected mixed-surface cases。
|
||||
|
||||
Blocking issue:
|
||||
|
||||
1. Static inspection / `yoi plugin list/show` still evaluates unselected surfaces and can mark valid partial-surface enablement as rejected。
|
||||
- Runtime install now filters enabled surfaces, but static inspection still treats all declared manifest Tool / Service / Ingress surfaces as active eligibility requirements。
|
||||
- `PluginStaticInspection::statically_eligible()` requires all inspected tools, services, and ingresses to be eligible。
|
||||
- `inspect_resolved_plugin_static()` still iterates over all declared manifest tools/services/ingresses rather than selected/enabled surfaces。
|
||||
- `yoi plugin list/show` uses this unfiltered static inspection for resolved items。
|
||||
- Status calculation can classify the resolved item as `rejected` when no usable Tool exists and `static_eligible` is false。
|
||||
|
||||
Concrete failure mode:
|
||||
- Mixed package declares Tool + Service。
|
||||
- Enablement selects only Service。
|
||||
- Service grants are present; Tool grants are absent because Tool is not selected。
|
||||
- Runtime install correctly ignores unselected Tool。
|
||||
- Static inspection still checks unselected Tool and records missing Tool grant diagnostics, so `static_eligible = false`。
|
||||
- For service-only selection with no usable Tool, `yoi plugin list/show` can report the resolved plugin as `rejected` even though selected Service surface is valid/installable。
|
||||
|
||||
Why this blocks approval:
|
||||
- Ticket requires Tool / Service / Ingress grants to be independent。
|
||||
- Unselected surfaces must not block selected surfaces。
|
||||
- Ticket also requires `yoi plugin check/list/show` to report legacy vs instance-capable/rejected surfaces accurately。
|
||||
- Runtime path is fixed, but inspection/status can still be blocked by unselected surfaces, creating authority/reporting mismatch。
|
||||
|
||||
Required fix:
|
||||
- Make resolved static inspection eligibility account for `record.enabled_surfaces`。
|
||||
- Alternatively, separate declared-surface inspection from enabled-surface inspection and compute resolved `list/show` status from enabled surfaces only。
|
||||
- Keep `plugin check` free to inspect full package declaration if appropriate, but `plugin list/show` for a resolved enablement must not mark selected Service/Ingress invalid because unrelated unselected Tool grants are absent。
|
||||
- Add focused `yoi plugin` tests for mixed-surface partial enablement status/reporting。
|
||||
|
||||
Non-blocking concern:
|
||||
- Runtime install tests now cover partial enablement, but focused `yoi plugin list/show` tests are still missing and should be added with the fix。
|
||||
|
||||
Reviewer validation:
|
||||
- `cargo fmt --check`: passed。
|
||||
- `git diff --check 5ec8bae9..HEAD`: passed。
|
||||
- Template cargo-check: passed。
|
||||
- `cargo test -p manifest plugin -- --nocapture`: passed。
|
||||
- `cargo test -p pod plugin -- --nocapture`: passed。
|
||||
- `cargo test -p yoi plugin -- --nocapture`: passed。
|
||||
- `cargo check -p yoi`: passed。
|
||||
- `cargo check -p yoi-plugin-pdk`: passed。
|
||||
- `yoi ticket doctor`: passed。
|
||||
- `nix build .#yoi --no-link`: passed。
|
||||
|
||||
Worktree status at end: clean。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T15:13:56Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Coder r3-fix report received from `yoi-coder-00001KVJHYP4Q`.
|
||||
|
||||
New fix commit:
|
||||
- `627c8f36 plugin: filter static enabled surfaces`
|
||||
|
||||
Branch commits now:
|
||||
- `627c8f36 plugin: filter static enabled surfaces`
|
||||
- `79ca0f7f plugin: enforce enabled lifecycle surfaces`
|
||||
- `870bcc76 plugin: fix instance lifecycle blockers`
|
||||
- `147a6005 plugin: add instance lifecycle surface`
|
||||
|
||||
Fix summary:
|
||||
- `crates/pod/src/feature/plugin.rs`:
|
||||
- `inspect_resolved_plugin_static()` now filters Tool / Service / Ingress inspection by `record.enabled_surfaces`。
|
||||
- `PluginStaticInspection::statically_eligible()` therefore reflects enabled/selected surfaces only for resolved Plugin records。
|
||||
- `plugin check` remains full-declaration oriented because check-time inspection uses all declared manifest surfaces as enabled。
|
||||
- `crates/yoi/src/plugin_cli.rs`:
|
||||
- Resolved `plugin list/show` diagnostics now use filtered static inspection。
|
||||
- Added focused CLI inspection test for mixed Tool+Service package where only Service is enabled and Tool grants are absent:
|
||||
- status remains `active`。
|
||||
- static eligibility remains true。
|
||||
- unselected Tool is not listed/reported。
|
||||
- unselected Tool missing grants do not produce diagnostics/rejection。
|
||||
|
||||
Coder validation reported as passing:
|
||||
- `cargo test -p manifest plugin -- --nocapture`
|
||||
- `cargo test -p pod plugin -- --nocapture`
|
||||
- `cargo test -p yoi plugin -- --nocapture`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo check -p yoi-plugin-pdk`
|
||||
- `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml`
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check 5ec8bae9..HEAD`
|
||||
- `yoi ticket doctor`
|
||||
- `nix build .#yoi --no-link`
|
||||
|
||||
Orchestrator evidence checked before r4 review:
|
||||
- Implementation worktree is clean。
|
||||
- HEAD is `627c8f36`。
|
||||
- Fix diff from `79ca0f7f..HEAD` touches `crates/pod/src/feature/plugin.rs` and `crates/yoi/src/plugin_cli.rs`, about 209 insertions / 76 deletions。
|
||||
- Overall `git diff --check 5ec8bae9..HEAD` produced no diagnostics。
|
||||
|
||||
Next action:
|
||||
- Route back to Reviewer for r4 verification of static inspection / `plugin list/show` partial-surface reporting and final acceptance criteria。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: review author: yoi-reviewer-00001KVJHYP4Q-r1 at: 2026-06-20T15:18:34Z status: approve -->
|
||||
|
||||
## Review: approve
|
||||
|
||||
Verdict: `approve`
|
||||
|
||||
確認範囲:
|
||||
- Ticket contract:
|
||||
- `.yoi/tickets/00001KVJHYP4Q/item.md`
|
||||
- `.yoi/tickets/00001KVJHYP4Q/thread.md`
|
||||
- r4 fix diff: `79ca0f7f..627c8f36`
|
||||
- overall implementation shape: `5ec8bae9..627c8f36`
|
||||
- Focused files:
|
||||
- `crates/pod/src/feature/plugin.rs`
|
||||
- `crates/yoi/src/plugin_cli.rs`
|
||||
- `crates/manifest/src/plugin.rs`
|
||||
- `crates/plugin-pdk/src/lib.rs`
|
||||
- `resources/plugin/templates/rust-component-instance/*`
|
||||
- `resources/plugin/wit/yoi-plugin-instance-v1.wit`
|
||||
|
||||
r3 blocker verification:
|
||||
- Previous static-inspection/list-show blocker is resolved。
|
||||
- `inspect_resolved_plugin_static()` now filters Tool / Service / Ingress eligibility by `record.enabled_surfaces`。
|
||||
- `PluginStaticInspection::statically_eligible()` now operates over filtered/selected surface vectors for resolved records。
|
||||
- `plugin check` remains full-declaration oriented because check-time inspection constructs the record with all declared manifest surfaces enabled。
|
||||
- Resolved `plugin list/show` diagnostics now use filtered static inspection and include selected Service/Ingress diagnostics。
|
||||
- Focused CLI test `service_only_enablement_ignores_unselected_tool_static_grants` confirms a mixed Tool+Service package with only Service enabled remains `active`, is statically eligible, does not report the unselected Tool, and does not emit missing Tool grant diagnostics。
|
||||
|
||||
Earlier blocker re-check:
|
||||
- Instance PDK/template shape, lifecycle returned JSON/status handling, Service/Ingress instance retention, installed-path ingress dispatch, and per-surface runtime guards remain present at a high level。
|
||||
|
||||
Blocking issues:
|
||||
- None found。
|
||||
|
||||
Non-blocking follow-ups:
|
||||
- None requiring this Ticket to remain blocked。
|
||||
|
||||
Reviewer validation passed:
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check 5ec8bae9..HEAD`
|
||||
- `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml`
|
||||
- `cargo test -p manifest plugin -- --nocapture`
|
||||
- `cargo test -p pod plugin -- --nocapture`
|
||||
- `cargo test -p yoi plugin -- --nocapture`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo check -p yoi-plugin-pdk`
|
||||
- `/home/hare/Projects/yoi/target/debug/yoi ticket doctor`
|
||||
- `nix build .#yoi --no-link`
|
||||
|
||||
Template check generated `resources/plugin/templates/rust-component-instance/Cargo.lock`; reviewer removed it after validation。
|
||||
|
||||
Worktree status:
|
||||
- Clean at commit `627c8f36` on `impl/00001KVJHYP4Q-plugin-instance-lifecycle`。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T15:22:49Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Reviewer r4 approvalを受けて、実装ブランチを Orchestrator worktree の `orchestration` branch に統合した。
|
||||
|
||||
統合:
|
||||
- Merge commit: `43c9216e merge: plugin instance lifecycle surface`
|
||||
- Merged branch: `impl/00001KVJHYP4Q-plugin-instance-lifecycle`
|
||||
- Implementation commits:
|
||||
- `147a6005 plugin: add instance lifecycle surface`
|
||||
- `870bcc76 plugin: fix instance lifecycle blockers`
|
||||
- `79ca0f7f plugin: enforce enabled lifecycle surfaces`
|
||||
- `627c8f36 plugin: filter static enabled surfaces`
|
||||
|
||||
Orchestrator worktree validation:
|
||||
- `cargo fmt --check`: passed
|
||||
- `git diff --check HEAD^1..HEAD`: passed
|
||||
- `cargo test -p manifest plugin -- --nocapture`: passed
|
||||
- `cargo test -p pod plugin -- --nocapture`: passed
|
||||
- `cargo test -p yoi plugin -- --nocapture`: passed
|
||||
- `cargo check -p yoi`: passed
|
||||
- `cargo check -p yoi-plugin-pdk`: passed
|
||||
- `CARGO_TARGET_DIR=target/review-template cargo check --manifest-path resources/plugin/templates/rust-component-instance/Cargo.toml`: passed
|
||||
- `/home/hare/Projects/yoi/target/debug/yoi ticket doctor`: passed
|
||||
- `nix build .#yoi --no-link`: passed
|
||||
|
||||
Template validation generated `resources/plugin/templates/rust-component-instance/Cargo.lock`; it was removed after validation. Final Orchestrator worktree status is clean。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-20T15:22:53Z from: inprogress to: done reason: implementation_merged_and_validated field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Reviewer approval、Orchestrator worktree への統合、必要な検証が完了したため `done` に遷移する。実装は Plugin instance lifecycle surface、Service/Ingress declarations and grants、PDK/template、static/runtime inspection、legacy compatibility、および focused tests を含む。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T15:23:11Z from: done to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T15:23:11Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
Plugin Service/Ingress component lifecycle surface を実装し、Orchestrator worktree の `orchestration` branch に統合した。
|
||||
|
||||
主な成果:
|
||||
- Pod plugin feature に host-managed `PluginInstanceRegistry` / instance handle 境界を追加し、Tool dispatch を instance 経由に変更。
|
||||
- New instance-capable component world `yoi:plugin/instance@1.0.0` と WIT resource を追加。
|
||||
- `yoi-plugin-pdk` と Rust component template に instance-oriented authoring support を追加。
|
||||
- Existing component Tool world / raw wasm Tool runtime を instance registry compatibility path に維持。
|
||||
- Manifest/static validation に Service / Ingress declarations と per-surface grant validation を追加。
|
||||
- Service lifecycle/status/diagnostics と bounded in-process ingress dispatch path を実装。
|
||||
- Tool / Service / Ingress enabled-surface filtering を runtime install, dispatch guard, and resolved static inspection / `yoi plugin list/show` に適用。
|
||||
- `plugin check` は package declaration inspection、resolved `plugin list/show` は selected/enabled surfaces に基づく reporting に分離。
|
||||
- Focused tests added for manifest validation, legacy Tool compatibility, instance state persistence, ingress dispatch, Service/Ingress grant denial, failure diagnostics, and partial enabled-surface static reporting。
|
||||
|
||||
統合・検証:
|
||||
- Merge commit: `43c9216e merge: plugin instance lifecycle surface`
|
||||
- Implementation commits: `147a6005`, `870bcc76`, `79ca0f7f`, `627c8f36`
|
||||
- Reviewer final verdict: approve
|
||||
- Validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p manifest plugin -- --nocapture`, `cargo test -p pod plugin -- --nocapture`, `cargo test -p yoi plugin -- --nocapture`, `cargo check -p yoi`, `cargo check -p yoi-plugin-pdk`, template cargo-check, `yoi ticket doctor`, and `nix build .#yoi --no-link`。
|
||||
|
||||
範囲外:
|
||||
- Discord Bridge 本体、public registry/install/update/signature tooling、arbitrary Plugin UI channel、hidden context injection、Service/Ingress による model-visible Tool schema mutation は実装していない。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T15:23:35Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Post-close cleanup completed。
|
||||
|
||||
- Stopped child Pods and reclaimed scope:
|
||||
- `yoi-coder-00001KVJHYP4Q`
|
||||
- `yoi-reviewer-00001KVJHYP4Q-r1`
|
||||
- Removed implementation worktree:
|
||||
- `/home/hare/Projects/yoi/.worktree/00001KVJHYP4Q-plugin-instance-lifecycle`
|
||||
- Deleted implementation branch:
|
||||
- `impl/00001KVJHYP4Q-plugin-instance-lifecycle`
|
||||
- Orchestrator worktree remains clean on `orchestration` at `bc484338`。
|
||||
|
||||
Root/original workspace was not used for merge/validation/cleanup operations beyond observing the worktree list output from the Orchestrator worktree command。
|
||||
|
||||
---
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"id":"orch-plan-20260620-133128-1","ticket_id":"00001KVJKHAFE","kind":"accepted_plan","accepted_plan":{"summary":"`yoi mcp` CLI namespaceを追加し、resolved MCP server config、trust policy、static diagnostics、provider-discovered tools/resources/prompts eligibility、live/unavailable stateを bounded human/JSON outputで inspectionできるようにする。CLIはMCP serverを起動せず、tools/call/resources/read/prompts/getを直接実行しない。","branch":"impl/00001KVJKHAFE-mcp-cli-inspection","worktree":"/home/hare/Projects/yoi/.worktree/00001KVJKHAFE-mcp-cli-inspection","role_plan":"Orchestrator は Plugin instance lifecycle work と並行して専用 implementation worktree `.worktree/00001KVJKHAFE-mcp-cli-inspection` を作成し、Coder をその child worktree への narrow write scope で起動する。Coder 実装後、Reviewer が read-only inspection境界、no server spawn/no tool execution/no content fetch、secret/content redaction、static-vs-live status表現、JSON shape、help/test/Nixを確認する。"},"author":"yoi-orchestrator","at":"2026-06-20T13:31:28Z"}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"relations": [
|
||||
{
|
||||
"ticket_id": "00001KVJKHAFE",
|
||||
"kind": "related",
|
||||
"target": "00001KVHR3WRF",
|
||||
"note": "CLI inspection reads resolved MCP server config/trust policy",
|
||||
"author": "yoi ticket",
|
||||
"at": "2026-06-20T13:30:15Z"
|
||||
},
|
||||
{
|
||||
"ticket_id": "00001KVJKHAFE",
|
||||
"kind": "related",
|
||||
"target": "00001KVHR3WRY",
|
||||
"note": "CLI inspection reports lifecycle/client initialization diagnostics",
|
||||
"author": "yoi ticket",
|
||||
"at": "2026-06-20T13:30:15Z"
|
||||
},
|
||||
{
|
||||
"ticket_id": "00001KVJKHAFE",
|
||||
"kind": "related",
|
||||
"target": "00001KVHR3WS6",
|
||||
"note": "CLI tools output reports provider-discovered ToolRegistry registration eligibility",
|
||||
"author": "yoi ticket",
|
||||
"at": "2026-06-20T13:30:16Z"
|
||||
},
|
||||
{
|
||||
"ticket_id": "00001KVJKHAFE",
|
||||
"kind": "related",
|
||||
"target": "00001KVHR3WSN",
|
||||
"note": "CLI resources/prompts output reports explicit operation eligibility",
|
||||
"author": "yoi ticket",
|
||||
"at": "2026-06-20T13:30:16Z"
|
||||
},
|
||||
{
|
||||
"ticket_id": "00001KVJKHAFE",
|
||||
"kind": "related",
|
||||
"target": "00001KVHR3WSW",
|
||||
"note": "CLI shows list_changed refresh/reinitialize diagnostics",
|
||||
"author": "yoi ticket",
|
||||
"at": "2026-06-20T13:30:16Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
---
|
||||
title: 'MCP: add yoi CLI inspection commands'
|
||||
state: 'closed'
|
||||
created_at: '2026-06-20T13:29:16Z'
|
||||
updated_at: '2026-06-20T13:56:39Z'
|
||||
assignee: null
|
||||
queued_by: 'workspace-panel'
|
||||
queued_at: '2026-06-20T13:31:00Z'
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
MCP local stdio integration は server config / lifecycle client / ToolRegistry registration / tools-call / resources-prompts explicit operations / list_changed diagnostics まで進んだ。一方で、ユーザーが `yoi` CLI から MCP の設定・解決結果・接続状態・discovered capabilities を確認する read-only inspection surface がまだ整理されていない。
|
||||
|
||||
Plugin には `yoi plugin list/show` があるが、MCP は plugin model そのものではなく protocol-bound bridge/runtime kind として扱う決定になっている。そのため MCP には専用の CLI namespace を用意し、設定済み server と provider-discovered tools/resources/prompts、起動/初期化/refresh diagnostics を確認できるようにする。
|
||||
|
||||
## 要件
|
||||
|
||||
- `yoi mcp ...` もしくは同等の top-level CLI namespace を追加する。
|
||||
- 初期 scope は read-only inspection とし、MCP server process の起動・Tool 実行・resources/prompts の content fetch は通常 runtime/tool path を迂回して行わない。
|
||||
- 最低限のコマンドを提供する。
|
||||
- `yoi mcp list` または `yoi mcp server list`: resolved MCP server の一覧。
|
||||
- `yoi mcp show <server>`: server config/ref、transport、trust policy、resolved status、diagnostics、capability summary。
|
||||
- `yoi mcp tools [<server>]`: provider-discovered MCP tools と Yoi 側 stable tool name / registration eligibility / diagnostics。
|
||||
- `yoi mcp resources [<server>]`: discovered resources/resource templates の summary と explicit operation eligibility。
|
||||
- `yoi mcp prompts [<server>]`: discovered prompts の summary と explicit operation eligibility。
|
||||
- 各コマンドは human-readable output と `--json` を持つ。
|
||||
- `--workspace <PATH>` と `--profile <REF>` を Plugin CLI と同じ感覚で扱い、resolved Profile/config から MCP server configuration を読む。
|
||||
- 出力は bounded / content-safe にする。
|
||||
- resource contents、prompt full text、secret values、environment secret refs の解決結果は表示しない。
|
||||
- external server 由来の descriptions/schema/annotations は untrusted data として扱い、必要なら truncate する。
|
||||
- 接続済み live Pod state が必要な情報と static config inspection だけで得られる情報を区別する。
|
||||
- static mode では config/resolution/package-less diagnostics を表示する。
|
||||
- live state が未実装または unavailable の場合は、silent stale state ではなく `not live / unavailable` を明示する。
|
||||
- `notifications/*/list_changed` 由来の restart/reinitialize-required diagnostics がある場合は CLI で見えるようにする。
|
||||
- CLI help に MCP namespace を追加する。
|
||||
|
||||
## Non-goals
|
||||
|
||||
- MCP tool を CLI から直接実行すること。
|
||||
- MCP resources/prompts の content を CLI から直接取得して表示すること。
|
||||
- MCP server の install/update/distribution。
|
||||
- Streamable HTTP / remote auth / OAuth の実装。
|
||||
- sampling / elicitation の実行または approval flow。
|
||||
- Plugin CLI に MCP を混ぜること。
|
||||
|
||||
## 受け入れ条件
|
||||
|
||||
- `yoi --help` に MCP CLI namespace が表示される。
|
||||
- `yoi mcp list --json` が resolved MCP server の bounded structured report を返す。
|
||||
- `yoi mcp show <server> --json` が server identity、transport kind、trust policy summary、capabilities summary、diagnostics を返す。
|
||||
- `yoi mcp tools [<server>] --json` が Yoi stable tool name と MCP server/tool identity、schema availability、registration status/diagnostics を返す。
|
||||
- `yoi mcp resources [<server>] --json` と `yoi mcp prompts [<server>] --json` が content を取得せず summary/eligibility を返す。
|
||||
- Human-readable output は empty/missing/invalid/unavailable を区別して表示する。
|
||||
- Secrets and resource/prompt contents are not printed in normal or JSON output.
|
||||
- Focused CLI tests cover list/show/tools/resources/prompts, missing server, invalid config, and JSON output.
|
||||
- Validation before completion includes `cargo fmt --check`, focused MCP/CLI tests, `cargo check`, `git diff --check`, `yoi ticket doctor`, and `nix build .#yoi --no-link`.
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
## Resolution
|
||||
|
||||
`00001KVJKHAFE` を完了しました。
|
||||
|
||||
実装内容:
|
||||
- `yoi mcp` CLI namespace を追加しました。
|
||||
- Read-only inspection commands を追加しました。
|
||||
- `yoi mcp list`
|
||||
- `yoi mcp show <server>`
|
||||
- `yoi mcp tools [<server>]`
|
||||
- `yoi mcp resources [<server>]`
|
||||
- `yoi mcp prompts [<server>]`
|
||||
- Human-readable output と `--json` output を追加しました。
|
||||
- Inspection は static/resolved config のみを扱い、MCP server process を起動しません。
|
||||
- `tools/call`, `resources/read`, `prompts/get` は実行しません。
|
||||
- Live/provider-discovered state は `not_live` / `unavailable` と明示します。
|
||||
- Env values, secret refs, env refs, args, resource content, prompt content は redacted/omitted します。
|
||||
- Resource/prompt operation eligibility は content fetch なしで報告します。
|
||||
- MCP namespace は Plugin CLI namespace と分離したままです。
|
||||
|
||||
主な commit:
|
||||
- `c91f5fc9 mcp: add cli inspection`
|
||||
- `5e0b023a merge: mcp cli inspection`
|
||||
|
||||
Review:
|
||||
- r1 は `approve`。
|
||||
- Reviewer は read-only boundary、no process start、no tools/resource/prompt content fetch、static-vs-live unavailable state、secret/content redaction、MCP namespace separation、help/tests を確認しました。
|
||||
|
||||
最終 validation:
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check HEAD^1..HEAD`
|
||||
- `cargo test -p yoi mcp`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo run -q -p yoi -- --help` + MCP command grep
|
||||
- `TicketDoctor`: 0 errors
|
||||
|
||||
Known unrelated note:
|
||||
- `TicketDoctor` は既存 Ticket の warning 4 件を返しましたが、この Ticket の変更とは無関係です。
|
||||
|
||||
Nix validation:
|
||||
- Not run because no dependency/package/source-filter files changed。
|
||||
|
||||
Validation log:
|
||||
- `/run/user/1000/yoi/yoi-orchestrator/bash-output/bash-xrqves.log`
|
||||
|
|
@ -1,331 +0,0 @@
|
|||
<!-- event: create author: "yoi ticket" at: 2026-06-20T13:29:16Z -->
|
||||
|
||||
## 作成
|
||||
|
||||
LocalTicketBackend によって作成されました。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: intake_summary author: hare at: 2026-06-20T13:30:16Z -->
|
||||
|
||||
## Intake summary
|
||||
|
||||
Marked ready by `yoi ticket state`.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: "yoi ticket" at: 2026-06-20T13:30:16Z from: planning to: ready reason: cli_state field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Marked ready by `yoi ticket state`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: workspace-panel at: 2026-06-20T13:31:00Z from: ready to: queued reason: queued field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を `workspace-panel` が queued にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: decision author: yoi-orchestrator at: 2026-06-20T13:31:52Z -->
|
||||
|
||||
## Decision
|
||||
|
||||
Routing decision: implementation_ready_parallel
|
||||
|
||||
Reason:
|
||||
- Workspace Dashboard Queue により、この Ticket は Orchestrator routing 対象として明示許可された。
|
||||
- User standing directive: blocker が無いものは並列実行する。現在 `00001KVJHYP4Q` Plugin instance lifecycle Coder が inprogress だが、この Ticket は MCP CLI inspection namespaceであり直接 dependency/conflict はない。Potential shared CLI parser edits are manageable in separate worktree and will be resolved at merge/review boundary。
|
||||
- Ticket body は `yoi mcp` namespace、list/show/tools/resources/prompts commands、human/JSON output、workspace/profile resolution、read-only inspection boundary、static-vs-live availability、secret/content redaction、acceptance testsを実装可能な粒度で定義している。
|
||||
- 未解決 blocker relation はない。Relations are `related` context to completed MCP foundation Tickets。
|
||||
- Orchestrator worktree は clean、matching branch/worktree はなし。
|
||||
- Risk domain は CLI / MCP / inspection / secrets / read-only boundary だが、Ticket は no server process start、no tool execution、no resources/prompts content fetch、secrets/content non-printing、bounded outputを明示している。bounded context check 後も implementation 前に必要な追加 human decision は見つからなかった。
|
||||
|
||||
Evidence checked:
|
||||
- Ticket `00001KVJKHAFE` body / thread / relations / artifacts。
|
||||
- `TicketRelationQuery(00001KVJKHAFE)`: only non-blocking `related` records to completed MCP config/lifecycle/tools/resources/list_changed Tickets。
|
||||
- `TicketOrchestrationPlanQuery(00001KVJKHAFE)`: no previous plan records; accepted plan recorded now。
|
||||
- Workspace state:
|
||||
- Orchestrator worktree clean at `142fdffb`。
|
||||
- queued: this Ticket only。
|
||||
- inprogress: `00001KVJHYP4Q` Plugin instance lifecycle。
|
||||
- visible spawned child: `yoi-coder-00001KVJHYP4Q` running。
|
||||
- no matching MCP CLI branch/worktree。
|
||||
|
||||
IntentPacket:
|
||||
|
||||
Intent:
|
||||
- Add read-only `yoi mcp` CLI inspection namespace for configured/resolved MCP servers and provider-discovered tools/resources/prompts eligibility。
|
||||
- Provide bounded human-readable and JSON reports without bypassing runtime Tool/resource/prompt paths。
|
||||
|
||||
Binding decisions / invariants:
|
||||
- CLI inspection must not start MCP server processes。
|
||||
- CLI inspection must not call MCP tools or fetch resource/prompt content。
|
||||
- Static config/resolution and live Pod state must be distinguished explicitly。
|
||||
- If live state is unavailable/unimplemented, output must say `not live` / `unavailable`, not silently stale。
|
||||
- Secrets, resolved secret/env values, resource contents, and prompt full text must not be printed in human or JSON output。
|
||||
- External server descriptions/schemas/annotations are untrusted and bounded/truncated。
|
||||
- Keep MCP separate from Plugin CLI namespace。
|
||||
- No Streamable HTTP/OAuth/sampling/elicitation/install/update/distribution implementation。
|
||||
|
||||
Requirements / acceptance criteria:
|
||||
- `yoi --help` shows MCP CLI namespace。
|
||||
- `yoi mcp list --json` returns resolved MCP server bounded structured report。
|
||||
- `yoi mcp show <server> --json` returns server identity, transport kind, trust policy summary, capabilities summary, diagnostics。
|
||||
- `yoi mcp tools [<server>] --json` returns Yoi stable tool name, MCP server/tool identity, schema availability, registration status/diagnostics。
|
||||
- `yoi mcp resources [<server>] --json` and `yoi mcp prompts [<server>] --json` return summaries/eligibility without content fetch。
|
||||
- Human-readable output distinguishes empty/missing/invalid/unavailable。
|
||||
- Focused CLI tests cover list/show/tools/resources/prompts, missing server, invalid config, JSON output, and secret/content non-leakage。
|
||||
- Validation includes fmt, focused tests, check, diff, TicketDoctor, Nix build。
|
||||
|
||||
Next action:
|
||||
- Record `queued -> inprogress` and commit Ticket records before creating implementation worktree and spawning Coder。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-20T13:32:15Z from: queued to: inprogress reason: orchestrator_acceptance_parallel_mcp_cli_inspection field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket body/thread, relation metadata, Orchestrator worktree state, visible Pods, existing branch/worktree, and bounded MCP CLI inspection context were checked. There is no unresolved blocking dependency, no direct conflict with current Plugin instance lifecycle work, no matching worktree/branch, and no missing planning decision. Accepting this queued Ticket for parallel implementation before worktree/Pod side effects.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T13:33:06Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Implementation orchestration start:
|
||||
- Acceptance records were committed on Orchestrator branch: `12d7e69f ticket: accept mcp cli inspection`。
|
||||
- Created implementation worktree `/home/hare/Projects/yoi/.worktree/00001KVJKHAFE-mcp-cli-inspection` on branch `impl/00001KVJKHAFE-mcp-cli-inspection` at `12d7e69f`。
|
||||
- Spawned Coder Pod `yoi-coder-00001KVJKHAFE` with write scope limited to the implementation worktree and read-only root scope only for runtime workspace identity。
|
||||
- Coder task explicitly forbids root/original workspace implementation, MCP server process startup, tools/call/resource/prompt content fetch, Plugin CLI namespace mixing, merge, Ticket close, and cleanup。
|
||||
|
||||
Next action:
|
||||
- Wait for Coder implementation report, then inspect branch diff/validation evidence and route to Reviewer。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T13:48:38Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Coder implementation report received from `yoi-coder-00001KVJKHAFE`.
|
||||
|
||||
Implementation commit:
|
||||
- `c91f5fc9 mcp: add cli inspection`
|
||||
|
||||
Changed areas reported:
|
||||
- Added `crates/yoi/src/mcp_cli.rs`:
|
||||
- Read-only `yoi mcp` inspection reports。
|
||||
- Commands: `list`, `show`, `tools`, `resources`, `prompts`。
|
||||
- Human and `--json` output。
|
||||
- Static resolved config inspection only; no MCP server processes are started。
|
||||
- Live/provider-discovered state is explicitly reported as `not_live` / `unavailable`。
|
||||
- Env values, secret refs, env refs, args, resource content, and prompt content are redacted/omitted。
|
||||
- Resource/prompt operation eligibility is reported without content fetch。
|
||||
- Updated `crates/yoi/src/main.rs`:
|
||||
- Added `mcp` namespace to parser, dispatcher, help output, and parser tests。
|
||||
|
||||
Coder validation reported:
|
||||
- `cargo test -p yoi mcp`: passed。
|
||||
- `cargo check -p yoi`: passed。
|
||||
- `cargo fmt --check`: passed。
|
||||
- `git diff --check`: passed。
|
||||
- `cargo run -p yoi -- --help | grep -E 'yoi mcp (list|show|tools)'`: passed; help shows MCP namespace。
|
||||
- `yoi ticket doctor`: passed。
|
||||
|
||||
Package impact:
|
||||
- No dependency, lockfile, source-filter, or `package.nix` changes。
|
||||
- Nix not run because packaging inputs were not changed。
|
||||
|
||||
Known risks / deferrals:
|
||||
- Live Pod MCP state is not integrated; provider-discovered tools/resources/prompts are therefore reported as `not_live` / `unavailable` rather than stale or fabricated。
|
||||
- `tools` cannot list Yoi stable provider tool names without live discovery; JSON schema includes future item shape, but item list is empty in static mode。
|
||||
- Touched `crates/yoi/src/main.rs` CLI parser/help, so there is possible narrow conflict risk with parallel Plugin Service/Ingress work. No Plugin Service/Ingress files were touched。
|
||||
|
||||
Orchestrator evidence checked before review dispatch:
|
||||
- Implementation worktree is clean。
|
||||
- HEAD is `c91f5fc9`。
|
||||
- Diff from acceptance `12d7e69f..HEAD` is one implementation commit touching 2 files, about 1438 insertions / 1 deletion。
|
||||
- `git diff --check 12d7e69f..HEAD` produced no diagnostics。
|
||||
|
||||
Next action:
|
||||
- Dispatch Reviewer for r1 review against Ticket requirements, with focus on read-only inspection boundary, no process start/no tools-call/no resource-prompt content fetch, static vs live unavailable honesty, redaction, JSON/human shape, help/tests, and conflict risk。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: plan author: yoi-orchestrator at: 2026-06-20T13:49:28Z -->
|
||||
|
||||
## Plan
|
||||
|
||||
Review dispatch:
|
||||
- Spawned Reviewer Pod `yoi-reviewer-00001KVJKHAFE-r1` against implementation branch `impl/00001KVJKHAFE-mcp-cli-inspection`。
|
||||
- Review target commit: `c91f5fc9 mcp: add cli inspection`。
|
||||
- Review baseline: `12d7e69f`。
|
||||
- Reviewer task focuses on read-only inspection boundary, no process start/no tools-call/no resource/prompt content fetch, static vs live unavailable honesty, secret/content redaction, JSON/human report shape, namespace separation from Plugin CLI, help/tests, scope creep absence, and parallel CLI merge conflict risk。
|
||||
- Reviewer is instructed not to edit source, commit, merge, close the Ticket, or use TicketReview directly; it will report verdict/evidence back to Orchestrator。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: review author: yoi-reviewer-00001KVJKHAFE-r1 at: 2026-06-20T13:54:05Z status: approve -->
|
||||
|
||||
## Review: approve
|
||||
|
||||
Verdict: `approve`
|
||||
|
||||
確認範囲:
|
||||
- Ticket contract / Orchestrator IntentPacket。
|
||||
- Implementation diff: `12d7e69f..c91f5fc9`。
|
||||
- 主な対象:
|
||||
- `crates/yoi/src/mcp_cli.rs`
|
||||
- `crates/yoi/src/main.rs`
|
||||
- MCP CLI path の process spawn / MCP client / tool-call / resource-read / prompt-get behavior を検索。
|
||||
- `crates/yoi/src/main.rs` の merge conflict risk も確認。
|
||||
|
||||
Blocking issues: none。
|
||||
|
||||
Approval evidence:
|
||||
- MCP CLI は static/read-only に留まっている。
|
||||
- `mcp_cli.rs` は manifest/profile config types を使い、MCP runtime/client path を import していない。
|
||||
- `inspect_static_config` は Profile/config を resolve して static-only diagnostics を report し、stdio lifecycle/client API を呼ばない。
|
||||
- CLI implementation から `std::process::Command`, `Command::new`, `spawn`, MCP lifecycle/client calls, ToolRegistry calls, `tools/call` path は見つからない。
|
||||
- Resource/prompt content fetch はない。
|
||||
- `resources/read` / `prompts/get` は operation label / stable name としてだけ出ており、`registration_status: "not_live"`, `content_state: "not_fetched"` を返す。
|
||||
- Static vs live state is explicit。
|
||||
- JSON includes `inspection_mode` booleans: `starts_servers: false`, `calls_tools: false`, `fetches_resource_or_prompt_content: false`。
|
||||
- Reports include `live_state.status = "not_live"`; human output says `live: not live / unavailable`。
|
||||
- Secrets/args are redacted。
|
||||
- stdio args are summarized as `arg_count` plus `args_redacted: true`。
|
||||
- env config is summarized by counts/kinds with `values_redacted: true`; env literal values, secret refs, and env refs are not emitted。
|
||||
- Tests cover non-leakage for arg secret, literal env value, secret ref, and env ref。
|
||||
- MCP namespace remains separate。
|
||||
- `main.rs` adds distinct top-level `mcp` parser/dispatcher path; Plugin CLI is not mixed beyond adjacency in top-level help/parsing。
|
||||
- Top-level `yoi --help` shows MCP namespace and commands。
|
||||
|
||||
Non-blocking concerns / follow-ups:
|
||||
- Implementation is intentionally static-only. It does not surface real live provider-discovered tools/resources/prompts or live `notifications/*/list_changed` restart/reinitialize diagnostics. It clearly reports unavailable/not live, which matches this implementation boundary; future live-state inspection can extend it。
|
||||
- `yoi mcp --help` / subcommand help may flow through `ParseError` and error exit rather than normal help exit. Top-level `yoi --help` satisfies the stated acceptance criterion, so this is not blocking。
|
||||
|
||||
Reviewer validation:
|
||||
- `cargo fmt --check`: passed。
|
||||
- `git diff --check 12d7e69f..HEAD`: passed。
|
||||
- `cargo test -p yoi mcp`: passed, 8 tests。
|
||||
- `cargo check -p yoi`: passed。
|
||||
- `cargo run -p yoi -- --help`: passed; output includes `yoi mcp list`, `show`, and `tools|resources|prompts`。
|
||||
- `cargo run -p yoi -- ticket doctor`: passed, `doctor: ok`。
|
||||
- Conflict-risk check: `main.rs` merge risk low; trivial merge check produced no conflict markers。
|
||||
- `nix build .#yoi --no-link` not run because no package/source-filter concern was found。
|
||||
|
||||
Worktree status at review end: source tree clean; ignored `target/` only。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: implementation_report author: yoi-orchestrator at: 2026-06-20T13:56:02Z -->
|
||||
|
||||
## Implementation report
|
||||
|
||||
Merge and final validation report:
|
||||
|
||||
Merged implementation branch into Orchestrator branch:
|
||||
- Merge commit: `5e0b023a merge: mcp cli inspection`
|
||||
- Implementation commit included:
|
||||
- `c91f5fc9 mcp: add cli inspection`
|
||||
|
||||
Reviewer outcome:
|
||||
- r1 approved with no blocking issues。
|
||||
- Reviewer confirmed CLI inspection is static/read-only, does not start MCP server processes, does not call tools/resources/prompts, distinguishes static vs live unavailable state, redacts/omits secrets/env/args/content, keeps MCP namespace separate from Plugin CLI, and has focused tests。
|
||||
|
||||
Orchestrator validation after merge passed:
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check HEAD^1..HEAD`
|
||||
- `cargo test -p yoi mcp`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo run -q -p yoi -- --help` with targeted grep for MCP commands。
|
||||
- `TicketDoctor`: 0 errors, 4 existing warnings unrelated to this Ticket。
|
||||
|
||||
Validation log:
|
||||
- `/run/user/1000/yoi/yoi-orchestrator/bash-output/bash-xrqves.log`
|
||||
|
||||
Nix validation:
|
||||
- Not run because this Ticket changed only Rust source under `crates/yoi` and no dependency/package/source-filter files changed。
|
||||
|
||||
Final state:
|
||||
- Orchestrator worktree clean at `5e0b023a` after validation。
|
||||
- Implementation worktree remains available for cleanup after Ticket completion records are committed。
|
||||
- Plugin instance lifecycle Coder continues in parallel。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: yoi-orchestrator at: 2026-06-20T13:56:11Z from: inprogress to: done reason: merged_reviewed_validated field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Implementation was merged into Orchestrator branch at `5e0b023a`, review approved, and final Orchestrator validation passed: `cargo fmt --check`, `git diff --check HEAD^1..HEAD`, `cargo test -p yoi mcp`, `cargo check -p yoi`, top-level help grep for MCP commands, and `TicketDoctor` with 0 errors.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T13:56:39Z from: done to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T13:56:39Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
## Resolution
|
||||
|
||||
`00001KVJKHAFE` を完了しました。
|
||||
|
||||
実装内容:
|
||||
- `yoi mcp` CLI namespace を追加しました。
|
||||
- Read-only inspection commands を追加しました。
|
||||
- `yoi mcp list`
|
||||
- `yoi mcp show <server>`
|
||||
- `yoi mcp tools [<server>]`
|
||||
- `yoi mcp resources [<server>]`
|
||||
- `yoi mcp prompts [<server>]`
|
||||
- Human-readable output と `--json` output を追加しました。
|
||||
- Inspection は static/resolved config のみを扱い、MCP server process を起動しません。
|
||||
- `tools/call`, `resources/read`, `prompts/get` は実行しません。
|
||||
- Live/provider-discovered state は `not_live` / `unavailable` と明示します。
|
||||
- Env values, secret refs, env refs, args, resource content, prompt content は redacted/omitted します。
|
||||
- Resource/prompt operation eligibility は content fetch なしで報告します。
|
||||
- MCP namespace は Plugin CLI namespace と分離したままです。
|
||||
|
||||
主な commit:
|
||||
- `c91f5fc9 mcp: add cli inspection`
|
||||
- `5e0b023a merge: mcp cli inspection`
|
||||
|
||||
Review:
|
||||
- r1 は `approve`。
|
||||
- Reviewer は read-only boundary、no process start、no tools/resource/prompt content fetch、static-vs-live unavailable state、secret/content redaction、MCP namespace separation、help/tests を確認しました。
|
||||
|
||||
最終 validation:
|
||||
- `cargo fmt --check`
|
||||
- `git diff --check HEAD^1..HEAD`
|
||||
- `cargo test -p yoi mcp`
|
||||
- `cargo check -p yoi`
|
||||
- `cargo run -q -p yoi -- --help` + MCP command grep
|
||||
- `TicketDoctor`: 0 errors
|
||||
|
||||
Known unrelated note:
|
||||
- `TicketDoctor` は既存 Ticket の warning 4 件を返しましたが、この Ticket の変更とは無関係です。
|
||||
|
||||
Nix validation:
|
||||
- Not run because no dependency/package/source-filter files changed。
|
||||
|
||||
Validation log:
|
||||
- `/run/user/1000/yoi/yoi-orchestrator/bash-output/bash-xrqves.log`
|
||||
|
||||
---
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
title: 'Remove obsolete daemon crate'
|
||||
state: 'closed'
|
||||
created_at: '2026-06-20T13:36:13Z'
|
||||
updated_at: '2026-06-20T13:41:19Z'
|
||||
assignee: null
|
||||
queued_by: 'yoi ticket'
|
||||
queued_at: '2026-06-20T13:36:51Z'
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
`crates/daemon/` は long-lived Pod lifecycle management 用に予約されていた placeholder crate だが、実装責務を持たないまま放置されている。現在の Pod lifecycle / socket serving / CLI/TUI startup は他 crate が所有しており、daemon crate を workspace に残すことで Cargo workspace、Cargo.lock、検証対象、設計境界に不要なノイズが残っている。
|
||||
|
||||
`daemon` という名前の将来責務は、Plugin Service/Ingress や Pod lifecycle の設計が固まってから、具体的な authority boundary と work item に基づいて再導入する。
|
||||
|
||||
## 要件
|
||||
|
||||
- `crates/daemon/` を削除する。
|
||||
- root `Cargo.toml` の workspace `members` / `default-members` から `crates/daemon` を削除する。
|
||||
- `Cargo.lock` から空の `daemon` package entry が消えるように更新する。
|
||||
- placeholder crate に依存する build/test/package path が残っていないことを確認する。
|
||||
- TUI completion tests など、実体パスではなく fixture として `crates/daemon` を参照している箇所は、別の現存 crate 名に置き換える。
|
||||
- historical report の過去記録は必要がない限り追跡対象から削除しないが、active docs / build config は obsolete crate を前提にしない。
|
||||
|
||||
## 受け入れ条件
|
||||
|
||||
- `crates/daemon/` が repository から削除されている。
|
||||
- `cargo metadata` / `cargo check` が daemon workspace member 不在で成功する。
|
||||
- `cargo test -p tui` など daemon 名を fixture にしていたテストが通る。
|
||||
- repository の active build/config/code references に `crates/daemon` が残っていない。
|
||||
- Validation before completion includes `cargo fmt --check`, relevant `cargo test`/`cargo check`, `git diff --check`, `yoi ticket doctor`, and `nix build .#yoi --no-link`.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Removed obsolete placeholder crates/daemon workspace member. Updated Cargo workspace/default-members, Cargo.lock, TUI completion fixtures, and package.nix cargoHash. Validation passed: cargo fmt --check; cargo test -p tui; cargo check --workspace; git diff --check; yoi ticket doctor; nix build .#yoi --no-link.
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<!-- event: create author: "yoi ticket" at: 2026-06-20T13:36:13Z -->
|
||||
|
||||
## 作成
|
||||
|
||||
LocalTicketBackend によって作成されました。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: intake_summary author: hare at: 2026-06-20T13:36:51Z -->
|
||||
|
||||
## Intake summary
|
||||
|
||||
Marked ready by `yoi ticket state`.
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: "yoi ticket" at: 2026-06-20T13:36:51Z from: planning to: ready reason: cli_state field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Marked ready by `yoi ticket state`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: "yoi ticket" at: 2026-06-20T13:36:51Z from: ready to: queued reason: queued field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を `yoi ticket` が queued にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: "yoi ticket" at: 2026-06-20T13:36:51Z from: queued to: inprogress reason: cli_state field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
State changed to `inprogress`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: "yoi ticket" at: 2026-06-20T13:41:19Z from: inprogress to: done reason: cli_state field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
State changed to `done`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: hare at: 2026-06-20T13:41:19Z from: done to: closed reason: closed field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を closed にしました。
|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- event: close author: hare at: 2026-06-20T13:41:19Z status: closed -->
|
||||
|
||||
## 完了
|
||||
|
||||
Removed obsolete placeholder crates/daemon workspace member. Updated Cargo workspace/default-members, Cargo.lock, TUI completion fixtures, and package.nix cargoHash. Validation passed: cargo fmt --check; cargo test -p tui; cargo check --workspace; git diff --check; yoi ticket doctor; nix build .#yoi --no-link.
|
||||
|
||||
|
||||
---
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
---
|
||||
title: 'CLI: `resume` サブコマンド化と Pod 名の暗黙解釈廃止'
|
||||
state: 'queued'
|
||||
created_at: '2026-06-20T16:18:52Z'
|
||||
updated_at: '2026-06-20T16:29:26Z'
|
||||
assignee: null
|
||||
readiness: 'implementation_ready'
|
||||
risk_flags: ['cli-ux', 'pod-metadata', 'workspace-scope', 'backward-compatibility']
|
||||
queued_by: 'workspace-panel'
|
||||
queued_at: '2026-06-20T16:29:26Z'
|
||||
---
|
||||
|
||||
## User claims / request snapshot
|
||||
|
||||
- CLI コマンドを整理したい。
|
||||
- `-r` / `--resume` は、`resume` サブコマンドにしてよいのではないか。
|
||||
- top-level で subcommand になっていない文字列を打つと Pod 名として解釈される挙動をやめたい。
|
||||
- `resume` は Dashboard と同様に、通常は現在 workspace 内の Pod だけを表示したい。
|
||||
- `resume --all` のような明示オプションを付けたときだけ、host 上の全 Pod を一覧できるようにしたい。
|
||||
|
||||
## Confirmed facts / sources
|
||||
|
||||
- `crates/yoi/src/main.rs` の top-level parser は、既知 subcommand 以外を generic option/positional parsing に落とし、bare positional を `LaunchMode::PodName` にしている。
|
||||
- 既存 parser tests には、現在の暗黙 Pod 名解釈を固定するものがある: `parse_positional_name_uses_pod_name_mode`、`parse_dashboard_word_remains_a_pod_console_name_not_an_alias`、`memory_lint_with_other_second_word_remains_positional_pod_name`。
|
||||
- `yoi --help` は現在 `yoi [OPTIONS] [POD_NAME]` と `-r, --resume` を案内している。
|
||||
- `crates/tui/src/lib.rs` には `LaunchMode::Resume` があり、現在は `yoi -r` / `yoi --resume` の picker 動線として扱われている。
|
||||
- `crates/tui/src/console/mod.rs` の resume path は `picker::run()` を workspace 情報なしで呼んでいる。
|
||||
- `crates/tui/src/picker.rs` の picker は stored Pod metadata と live Pod registry を読んで `PodList::from_sources(...)` を作っており、現状は workspace filter ではない。
|
||||
- `crates/tui/src/pod_list.rs` には `PodList::from_workspace_sources(...)` があり、stored metadata の `workspace_root` が現在 workspace と一致する Pod に絞り、その Pod 名に対応する live entries だけを残す workspace-scoped list の既存部品がある。
|
||||
- 関連 closed Ticket:
|
||||
- `00001KSYW63V0`: product CLI ownership / dispatch cleanup。過去には `-r`, positional Pod name, `--pod` 挙動維持が acceptance に含まれていたが、今回の依頼はその CLI UX を再整理する後続変更として扱える。
|
||||
- `00001KSXXRRC8`: LLM-facing Pod listing/restore visibility semantics。host-wide Pod enumeration を安易に広げない方針が記録されている。今回の CLI `resume --all` は human CLI 明示操作として扱う必要がある。
|
||||
|
||||
## Unverified hypotheses
|
||||
|
||||
- Dashboard の Pod 表示と完全に同一の filtering semantics にするなら、`PodList::from_workspace_sources(...)` を resume picker でも再利用するのが自然。
|
||||
- `resume --all` は現在の picker 相当の host/data-dir wide behavior を明示 opt-in にする実装で足りる可能性が高い。
|
||||
- CLI parser の整理は `crates/yoi/src/main.rs` と `crates/tui/src/{lib.rs,console/mod.rs,picker.rs,pod_list.rs}` の focused change で収まりそう。
|
||||
|
||||
## Undecided points / open questions
|
||||
|
||||
- この Ticket では、`-r` / `--resume` を互換 alias として残さず、`yoi resume` に寄せる方針を binding decision とする。互換 alias が必要になった場合は escalate する。
|
||||
- `yoi resume --all` の表示上、host-wide であることを明示する label / warning を入れるかは実装判断でよいが、Reviewer focus に含める。
|
||||
|
||||
## Background
|
||||
|
||||
現在の top-level CLI は、bare word を Pod 名として扱うため、未知 subcommand の typo や将来追加したい command 名が Pod Console 起動に化けやすい。`resume` を明示 subcommand にし、Pod 名指定は `--pod <NAME>` など明示 path に寄せると、CLI の command surface が読みやすくなる。
|
||||
|
||||
## Requirements
|
||||
|
||||
- `yoi resume` サブコマンドを追加し、現在の `-r` / `--resume` picker 動線を移す。
|
||||
- `yoi resume` は通常、現在の `--workspace <PATH>` / cwd workspace に属する Pod だけを表示する。
|
||||
- `yoi resume --all` は明示 opt-in として、host/data-dir 上の全 Pod を表示できる。
|
||||
- top-level の bare positional Pod name 解釈を廃止する。
|
||||
- `yoi dashboard`、`yoi memory other`、`yoi unknown` のような未知 top-level word は Pod 名として扱わず、unknown command / usage error にする。
|
||||
- Pod 名を直接開く導線は、既存の明示 `--pod <NAME>` を残す。
|
||||
- `yoi` 引数なしの default Console 起動は、この Ticket では変更しない。
|
||||
- Help / usage / parser tests を新しい command model に合わせる。
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- `yoi resume` が resume picker を開く。
|
||||
- `yoi resume --workspace <PATH>` が指定 workspace の Pod だけを表示する。
|
||||
- `yoi resume --all` が host/data-dir wide の Pod 一覧を表示する。
|
||||
- `yoi -r` / `yoi --resume` は unknown/deprecated error になり、help は `yoi resume` を案内する。
|
||||
- `yoi <bare-word>` は PodName mode にならず、未知 command として失敗する。
|
||||
- `yoi --pod <NAME>` は明示 Pod open/attach/restore/create path として残る。
|
||||
- `yoi --help` から `[POD_NAME]` の案内が消え、`yoi resume [--workspace <PATH>] [--all]` が表示される。
|
||||
- Focused parser tests が、旧 positional Pod name tests を置き換え、新しい `resume` / `resume --all` / unknown command 挙動を固定する。
|
||||
- Resume picker の workspace filtering について、stored metadata の `workspace_root` に基づく focused tests がある。
|
||||
|
||||
## Binding decisions / invariants
|
||||
|
||||
- Top-level bare word を Pod 名として推測しない。
|
||||
- Pod 名を開くには明示 `--pod <NAME>` を使う。
|
||||
- `resume --all` なしで host-wide Pod list を表示しない。
|
||||
- Workspace-scoped resume は Dashboard と同じ方向の semantics を使い、Pod metadata の `workspace_root` を authority にする。
|
||||
- Host-wide enumeration は human CLI の明示 `resume --all` に限定し、LLM-facing Pod tool visibility/scope とは混同しない。
|
||||
- 不必要な後方互換 alias は追加しない。
|
||||
|
||||
## Implementation latitude
|
||||
|
||||
- `LaunchMode::Resume` に workspace/all の追加情報を持たせるか、別の resume options 型を作るかは実装判断でよい。
|
||||
- picker API は `picker::run(options)` のように拡張してよい。
|
||||
- Workspace filter 実装は `PodList::from_workspace_sources(...)` の再利用を優先するが、Dashboard と厳密に同一化するために小さな helper 抽出をしてもよい。
|
||||
- Error wording / help wording は、ユーザーが `--pod` と `resume` を見つけやすい範囲で実装判断可。
|
||||
|
||||
## Readiness
|
||||
|
||||
- readiness: `implementation_ready`
|
||||
- risk_flags: [`cli-ux`, `pod-metadata`, `workspace-scope`, `backward-compatibility`]
|
||||
|
||||
## Escalation conditions
|
||||
|
||||
- `workspace_root` metadata がない legacy Pod を workspace-scoped resume に含めるべきか迷う場合。
|
||||
- `-r` / `--resume` を互換 alias として残す必要が出た場合。
|
||||
- `resume --all` が Pod scope / permission / LLM-visible tool visibility と混ざりそうな設計になる場合。
|
||||
- Parser change が `yoi pod ...`, `yoi ticket ...`, `yoi plugin ...`, `yoi memory lint ...`, `yoi panel`, `--session`, `--profile` の既存明示 path を壊しそうな場合。
|
||||
|
||||
## Validation
|
||||
|
||||
- `cargo fmt --check`
|
||||
- Focused parser tests in `-p yoi`
|
||||
- Focused TUI/pod-list picker tests in `-p tui`
|
||||
- `cargo check -p yoi -p tui`
|
||||
- `git diff --check`
|
||||
- `yoi ticket doctor`
|
||||
- 必要なら CLI smoke:
|
||||
- `target/debug/yoi --help`
|
||||
- `target/debug/yoi resume --help`
|
||||
- `target/debug/yoi --pod <test-name>` parser path
|
||||
- `target/debug/yoi unknown` が unknown command になること
|
||||
|
||||
## Related work
|
||||
|
||||
- Related closed Ticket: `00001KSYW63V0` — CLI ownership / dispatch cleanup
|
||||
- Related closed Ticket: `00001KSXXRRC8` — Pod listing / restore visibility semantics
|
||||
- Related files:
|
||||
- `crates/yoi/src/main.rs`
|
||||
- `crates/tui/src/lib.rs`
|
||||
- `crates/tui/src/console/mod.rs`
|
||||
- `crates/tui/src/picker.rs`
|
||||
- `crates/tui/src/pod_list.rs`
|
||||
- `crates/tui/src/workspace_panel.rs`
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<!-- event: create author: LocalTicketBackend at: 2026-06-20T16:18:52Z -->
|
||||
|
||||
## 作成
|
||||
|
||||
LocalTicketBackend によって作成されました。
|
||||
|
||||
---
|
||||
|
||||
<!-- event: state_changed author: workspace-panel at: 2026-06-20T16:29:26Z from: ready to: queued reason: queued field: state -->
|
||||
|
||||
## State changed
|
||||
|
||||
Ticket を `workspace-panel` が queued にしました。
|
||||
|
||||
|
||||
---
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -790,6 +790,15 @@ dependencies = [
|
|||
"phf 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "daemon"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"manifest",
|
||||
"protocol",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.23.0"
|
||||
|
|
@ -4288,6 +4297,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"crates/client",
|
||||
"crates/daemon",
|
||||
"crates/llm-worker",
|
||||
"crates/llm-worker-macros",
|
||||
"crates/session-store",
|
||||
|
|
@ -28,6 +29,7 @@ members = [
|
|||
]
|
||||
default-members = [
|
||||
"crates/client",
|
||||
"crates/daemon",
|
||||
"crates/llm-worker",
|
||||
"crates/llm-worker-macros",
|
||||
"crates/session-store",
|
||||
|
|
|
|||
10
crates/daemon/Cargo.toml
Normal file
10
crates/daemon/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "daemon"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
manifest = { workspace = true }
|
||||
protocol = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
25
crates/daemon/README.md
Normal file
25
crates/daemon/README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# daemon
|
||||
|
||||
## Role
|
||||
|
||||
`daemon` is reserved for future long-lived Pod lifecycle management.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Owns:
|
||||
|
||||
- daemon-specific lifecycle coordination when that design is implemented
|
||||
|
||||
Does not own today:
|
||||
|
||||
- current Pod socket serving (`pod`)
|
||||
- normal CLI/TUI startup (`yoi`, `tui`)
|
||||
- live registry mechanics already handled elsewhere (`pod-registry`)
|
||||
|
||||
## Design notes
|
||||
|
||||
This crate exists as a placeholder. Do not route current runtime authority through it until there is a concrete daemon design and work item.
|
||||
|
||||
## See also
|
||||
|
||||
- [`../../docs/design/pod-session-state.md`](../../docs/design/pod-session-state.md)
|
||||
1
crates/daemon/src/lib.rs
Normal file
1
crates/daemon/src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -57,40 +57,6 @@ pub const RUST_COMPONENT_TOOL_TEMPLATE: &[PluginTemplateResource] = &[
|
|||
},
|
||||
];
|
||||
|
||||
/// Embedded starter template for Rust Component Model instance Plugins.
|
||||
pub const RUST_COMPONENT_INSTANCE_TEMPLATE: &[PluginTemplateResource] = &[
|
||||
PluginTemplateResource {
|
||||
path: "Cargo.toml",
|
||||
contents: include_str!(
|
||||
"../../../resources/plugin/templates/rust-component-instance/Cargo.toml"
|
||||
),
|
||||
},
|
||||
PluginTemplateResource {
|
||||
path: "src/lib.rs",
|
||||
contents: include_str!(
|
||||
"../../../resources/plugin/templates/rust-component-instance/src/lib.rs"
|
||||
),
|
||||
},
|
||||
PluginTemplateResource {
|
||||
path: "plugin.toml",
|
||||
contents: include_str!(
|
||||
"../../../resources/plugin/templates/rust-component-instance/plugin.toml"
|
||||
),
|
||||
},
|
||||
PluginTemplateResource {
|
||||
path: "plugin.component.wasm",
|
||||
contents: include_str!(
|
||||
"../../../resources/plugin/templates/rust-component-instance/plugin.component.wasm"
|
||||
),
|
||||
},
|
||||
PluginTemplateResource {
|
||||
path: "README.md",
|
||||
contents: include_str!(
|
||||
"../../../resources/plugin/templates/rust-component-instance/README.md"
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct PluginConfig {
|
||||
|
|
@ -204,8 +170,6 @@ pub enum PluginPermission {
|
|||
Surface { surface: PluginSurface },
|
||||
Tool { name: String },
|
||||
ToolNamespace { namespace: String },
|
||||
Service { name: String },
|
||||
Ingress { name: String },
|
||||
ExternalWrite,
|
||||
HostApi { api: PluginHostApi },
|
||||
}
|
||||
|
|
@ -285,8 +249,6 @@ impl PluginPermission {
|
|||
Self::Surface { surface } => format!("surfaces.{surface}"),
|
||||
Self::Tool { name } => format!("tool.{name}"),
|
||||
Self::ToolNamespace { namespace } => format!("tool_namespace.{namespace}"),
|
||||
Self::Service { name } => format!("service.{name}"),
|
||||
Self::Ingress { name } => format!("ingress.{name}"),
|
||||
Self::ExternalWrite => "external_write".to_string(),
|
||||
Self::HostApi { api } => format!("host_api.{api}"),
|
||||
}
|
||||
|
|
@ -306,14 +268,6 @@ impl PluginPermission {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn service(name: impl Into<String>) -> Self {
|
||||
Self::Service { name: name.into() }
|
||||
}
|
||||
|
||||
pub fn ingress(name: impl Into<String>) -> Self {
|
||||
Self::Ingress { name: name.into() }
|
||||
}
|
||||
|
||||
pub fn host_api(api: PluginHostApi) -> Self {
|
||||
Self::HostApi { api }
|
||||
}
|
||||
|
|
@ -428,7 +382,7 @@ pub enum PluginIdParseError {
|
|||
InvalidLocalId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PluginPackageManifest {
|
||||
pub schema_version: u32,
|
||||
|
|
@ -444,10 +398,6 @@ pub struct PluginPackageManifest {
|
|||
pub hooks: Vec<PluginHookManifest>,
|
||||
#[serde(default)]
|
||||
pub tools: Vec<PluginToolManifest>,
|
||||
#[serde(default)]
|
||||
pub services: Vec<PluginServiceManifest>,
|
||||
#[serde(default)]
|
||||
pub ingresses: Vec<PluginIngressManifest>,
|
||||
/// Permission requests declared by the package. These are requests only;
|
||||
/// enablement grants must match them before runtime surfaces are exposed.
|
||||
#[serde(default)]
|
||||
|
|
@ -463,12 +413,6 @@ impl PluginPackageManifest {
|
|||
if !self.tools.is_empty() {
|
||||
surfaces.insert(PluginSurface::Tool);
|
||||
}
|
||||
if !self.services.is_empty() {
|
||||
surfaces.insert(PluginSurface::Service);
|
||||
}
|
||||
if !self.ingresses.is_empty() {
|
||||
surfaces.insert(PluginSurface::Ingress);
|
||||
}
|
||||
if self.runtime.is_some() {
|
||||
surfaces.insert(PluginSurface::Wasm);
|
||||
}
|
||||
|
|
@ -485,7 +429,6 @@ pub const PLUGIN_RUNTIME_WASM_ABI: &str = "yoi-plugin-wasm-1";
|
|||
/// packages remain explicit `kind = "wasm"` plus `abi = "yoi-plugin-wasm-1"`.
|
||||
pub const PLUGIN_RUNTIME_COMPONENT_KIND: &str = "wasm-component";
|
||||
pub const PLUGIN_COMPONENT_TOOL_WORLD: &str = "yoi:plugin/tool@1.0.0";
|
||||
pub const PLUGIN_COMPONENT_INSTANCE_WORLD: &str = "yoi:plugin/instance@1.0.0";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
|
|
@ -521,34 +464,6 @@ pub struct PluginToolManifest {
|
|||
pub external_write: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PluginServiceManifest {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
#[serde(default)]
|
||||
pub lifecycle: String,
|
||||
#[serde(default)]
|
||||
pub status_schema: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub side_effects: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PluginIngressManifest {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
#[serde(default)]
|
||||
pub event_kinds: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub input_schema: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub sources: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub side_effects: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PluginDiscoveryLimits {
|
||||
pub max_packages_per_store: usize,
|
||||
|
|
@ -599,7 +514,7 @@ impl PluginDiscoveryOptions {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DiscoveredPluginPackage {
|
||||
pub identity: SourceQualifiedPluginId,
|
||||
pub package_path: PathBuf,
|
||||
|
|
@ -614,19 +529,19 @@ pub struct DiscoveredPluginPackage {
|
|||
/// This is data-only metadata and bytes. Constructing it parses manifests and
|
||||
/// validates package/archive shape, but it does not load, instantiate, or
|
||||
/// execute Plugin code.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MaterializedPluginPackage {
|
||||
pub package: DiscoveredPluginPackage,
|
||||
pub files: BTreeMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PackedPluginPackage {
|
||||
pub output_path: PathBuf,
|
||||
pub package: DiscoveredPluginPackage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct PluginDiscoveryReport {
|
||||
pub packages: Vec<DiscoveredPluginPackage>,
|
||||
pub diagnostics: Vec<PluginDiagnostic>,
|
||||
|
|
@ -1157,10 +1072,7 @@ pub fn read_resolved_plugin_runtime_component(
|
|||
.with_package(&record.package_label)
|
||||
.with_digest(&record.digest));
|
||||
}
|
||||
if !matches!(
|
||||
runtime.world.as_deref(),
|
||||
Some(PLUGIN_COMPONENT_TOOL_WORLD) | Some(PLUGIN_COMPONENT_INSTANCE_WORLD)
|
||||
) {
|
||||
if runtime.world.as_deref() != Some(PLUGIN_COMPONENT_TOOL_WORLD) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
|
|
@ -2006,10 +1918,7 @@ fn validate_manifest(
|
|||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
if !matches!(
|
||||
runtime.world.as_deref(),
|
||||
Some(PLUGIN_COMPONENT_TOOL_WORLD) | Some(PLUGIN_COMPONENT_INSTANCE_WORLD)
|
||||
) {
|
||||
if runtime.world.as_deref() != Some(PLUGIN_COMPONENT_TOOL_WORLD) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Api,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
|
|
@ -2043,44 +1952,6 @@ fn validate_manifest(
|
|||
}
|
||||
}
|
||||
}
|
||||
let instance_capable = manifest.runtime.as_ref().is_some_and(|runtime| {
|
||||
runtime.kind == PLUGIN_RUNTIME_COMPONENT_KIND
|
||||
&& runtime.world.as_deref() == Some(PLUGIN_COMPONENT_INSTANCE_WORLD)
|
||||
});
|
||||
if (!manifest.services.is_empty() || !manifest.ingresses.is_empty()) && !instance_capable {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Surface,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin service/ingress declarations require the yoi:plugin/instance@1.0.0 component world",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
for service in &manifest.services {
|
||||
if !is_safe_id(&service.name) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Malformed,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin service name is not safe",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
}
|
||||
for ingress in &manifest.ingresses {
|
||||
if !is_safe_id(&ingress.name) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
PluginDiagnosticKind::Malformed,
|
||||
PluginDiagnosticPhase::Manifest,
|
||||
"plugin ingress name is not safe",
|
||||
)
|
||||
.with_source(source)
|
||||
.with_identity(SourceQualifiedPluginId::new(source, manifest.id.clone()))
|
||||
.with_package(label));
|
||||
}
|
||||
}
|
||||
for hook in &manifest.hooks {
|
||||
if !is_safe_id(&hook.id) {
|
||||
return Err(PluginDiagnostic::new(
|
||||
|
|
@ -2554,13 +2425,7 @@ mod tests {
|
|||
.collect();
|
||||
assert_eq!(
|
||||
paths,
|
||||
BTreeSet::from([
|
||||
"Cargo.toml",
|
||||
"src/lib.rs",
|
||||
"plugin.toml",
|
||||
"plugin.component.wasm",
|
||||
"README.md",
|
||||
])
|
||||
BTreeSet::from(["Cargo.toml", "src/lib.rs", "plugin.toml", "README.md"])
|
||||
);
|
||||
assert!(
|
||||
RUST_COMPONENT_TOOL_TEMPLATE
|
||||
|
|
@ -2586,86 +2451,6 @@ mod tests {
|
|||
assert_eq!(manifest.tools.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embedded_rust_component_instance_template_is_valid_package_shape() {
|
||||
let paths: BTreeSet<_> = RUST_COMPONENT_INSTANCE_TEMPLATE
|
||||
.iter()
|
||||
.map(|file| file.path)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
paths,
|
||||
BTreeSet::from([
|
||||
"Cargo.toml",
|
||||
"src/lib.rs",
|
||||
"plugin.toml",
|
||||
"plugin.component.wasm",
|
||||
"README.md"
|
||||
])
|
||||
);
|
||||
assert!(
|
||||
RUST_COMPONENT_INSTANCE_TEMPLATE
|
||||
.iter()
|
||||
.all(|file| !file.path.starts_with('/') && !file.path.contains(".."))
|
||||
);
|
||||
let manifest_text = RUST_COMPONENT_INSTANCE_TEMPLATE
|
||||
.iter()
|
||||
.find(|file| file.path == "plugin.toml")
|
||||
.unwrap()
|
||||
.contents;
|
||||
let manifest: PluginPackageManifest = toml::from_str(manifest_text).unwrap();
|
||||
assert_eq!(
|
||||
manifest.runtime.as_ref().unwrap().world.as_deref(),
|
||||
Some(PLUGIN_COMPONENT_INSTANCE_WORLD)
|
||||
);
|
||||
assert_eq!(manifest.services.len(), 1);
|
||||
assert_eq!(manifest.ingresses.len(), 1);
|
||||
assert!(
|
||||
manifest
|
||||
.declared_surfaces()
|
||||
.contains(&PluginSurface::Service)
|
||||
);
|
||||
assert!(
|
||||
manifest
|
||||
.declared_surfaces()
|
||||
.contains(&PluginSurface::Ingress)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_ingress_require_instance_component_world() {
|
||||
let manifest: PluginPackageManifest = toml::from_str(
|
||||
r#"
|
||||
schema_version = 1
|
||||
id = "bad.service"
|
||||
name = "Bad Service"
|
||||
version = "0.1.0"
|
||||
surfaces = ["service"]
|
||||
permissions = [{ kind = "surface", surface = "service" }, { kind = "service", name = "svc" }]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
world = "yoi:plugin/tool@1.0.0"
|
||||
component = "plugin.component.wasm"
|
||||
|
||||
[[services]]
|
||||
name = "svc"
|
||||
description = "bad"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let archive = StoredArchive {
|
||||
files: BTreeMap::from([("plugin.component.wasm".to_string(), b"placeholder".to_vec())]),
|
||||
};
|
||||
let err = validate_manifest(
|
||||
&manifest,
|
||||
&archive,
|
||||
"bad.service",
|
||||
PluginSourceKind::Project,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err.message.contains("service/ingress"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discovers_valid_user_and_workspace_packages() {
|
||||
let temp = TempDir::new().unwrap();
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ use serde_json::Value;
|
|||
|
||||
pub use wit_bindgen;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, ToolError>;
|
||||
|
||||
/// Current Yoi Component Model Tool world targeted by this PDK.
|
||||
pub const TOOL_WORLD: &str = "yoi:plugin/tool@1.0.0";
|
||||
|
||||
|
|
@ -100,10 +98,7 @@ impl ToolOutput {
|
|||
}
|
||||
|
||||
/// Create a Tool output whose content is typed JSON.
|
||||
pub fn json(
|
||||
summary: impl Into<String>,
|
||||
value: impl Serialize,
|
||||
) -> std::result::Result<Self, ToolError> {
|
||||
pub fn json(summary: impl Into<String>, value: impl Serialize) -> Result<Self, ToolError> {
|
||||
let content = serde_json::to_string(&value).map_err(ToolError::serialization)?;
|
||||
let output = Self {
|
||||
summary: normalize_summary(summary.into()),
|
||||
|
|
@ -297,7 +292,7 @@ impl ToolError {
|
|||
}
|
||||
|
||||
/// Parse the WIT `input-json` string into a typed input value.
|
||||
pub fn parse_json_input<T>(input_json: &str) -> std::result::Result<T, ToolError>
|
||||
pub fn parse_json_input<T>(input_json: &str) -> Result<T, ToolError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
|
|
@ -316,7 +311,7 @@ where
|
|||
pub fn run_json_tool<I, F>(tool_name: &str, input_json: &str, handler: F) -> String
|
||||
where
|
||||
I: DeserializeOwned,
|
||||
F: FnOnce(ToolContext, I) -> std::result::Result<ToolOutput, ToolError>,
|
||||
F: FnOnce(ToolContext, I) -> Result<ToolOutput, ToolError>,
|
||||
{
|
||||
let result = parse_json_input::<I>(input_json).and_then(|input| {
|
||||
let context = ToolContext::new(tool_name);
|
||||
|
|
@ -479,166 +474,3 @@ mod tests {
|
|||
assert!(HOST_WIT.contains("%list: func"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Versioned Component Model instance world handled by the host-managed
|
||||
/// PluginInstanceRegistry.
|
||||
pub const PLUGIN_INSTANCE_WORLD: &str = "yoi:plugin/instance@1.0.0";
|
||||
|
||||
/// Repository WIT for the current instance world.
|
||||
pub const INSTANCE_WIT: &str =
|
||||
include_str!("../../../resources/plugin/wit/yoi-plugin-instance-v1.wit");
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PluginIngressEvent {
|
||||
pub kind: String,
|
||||
pub source: String,
|
||||
#[serde(default)]
|
||||
pub payload: Value,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PluginStatus {
|
||||
pub state: String,
|
||||
#[serde(default)]
|
||||
pub data: Value,
|
||||
}
|
||||
|
||||
impl PluginStatus {
|
||||
pub fn ready(data: Value) -> Self {
|
||||
Self {
|
||||
state: "ready".to_string(),
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stopped() -> Self {
|
||||
Self {
|
||||
state: "stopped".to_string(),
|
||||
data: Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rust-facing instance Plugin contract. Hosts call `start` once, then route
|
||||
/// Tool/Ingress surfaces through the same mutable instance.
|
||||
pub trait Plugin: Sized + 'static {
|
||||
fn start(config: Value) -> Result<Self>;
|
||||
fn handle_tool(&mut self, name: &str, input: Value) -> Result<ToolOutput>;
|
||||
fn handle_ingress(&mut self, name: &str, event: PluginIngressEvent) -> Result<Value>;
|
||||
fn status(&self) -> Result<PluginStatus> {
|
||||
Ok(PluginStatus::ready(Value::Null))
|
||||
}
|
||||
fn stop(&mut self) -> Result<PluginStatus> {
|
||||
Ok(PluginStatus::stopped())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn plugin_instance_error(message: impl Into<String>) -> String {
|
||||
serde_json::json!({ "error": { "message": message.into() } }).to_string()
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn plugin_instance_status(status: &PluginStatus) -> String {
|
||||
serde_json::to_string(status).unwrap_or_else(|error| plugin_instance_error(error.to_string()))
|
||||
}
|
||||
|
||||
/// Implement the generated Component Model `Guest` trait for an instance Plugin
|
||||
/// and export it with the `wit-bindgen` generated `export!` macro.
|
||||
///
|
||||
/// The caller must invoke `wit_bindgen::generate!` for the `instance` world
|
||||
/// first, with `runtime_path: "yoi_plugin_pdk::wit_bindgen::rt"`. That defines
|
||||
/// the `Guest` trait and `export!` macro in the current module.
|
||||
#[macro_export]
|
||||
macro_rules! export_plugin_instance {
|
||||
($adapter:ident, $plugin:ty) => {
|
||||
struct $adapter;
|
||||
|
||||
thread_local! {
|
||||
static YOI_PLUGIN_INSTANCE: ::std::cell::RefCell<::std::option::Option<$plugin>> = const { ::std::cell::RefCell::new(None) };
|
||||
}
|
||||
|
||||
impl Guest for $adapter {
|
||||
fn start(config_json: ::std::string::String) -> ::std::string::String {
|
||||
let config = serde_json::from_str(&config_json).unwrap_or(serde_json::Value::Null);
|
||||
match <$plugin as $crate::Plugin>::start(config) {
|
||||
Ok(plugin) => {
|
||||
YOI_PLUGIN_INSTANCE.with(|slot| *slot.borrow_mut() = Some(plugin));
|
||||
$crate::plugin_instance_status(&$crate::PluginStatus::ready(serde_json::Value::Null))
|
||||
}
|
||||
Err(error) => $crate::plugin_instance_error(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_tool(
|
||||
name: ::std::string::String,
|
||||
input_json: ::std::string::String,
|
||||
) -> ::std::string::String {
|
||||
let input = serde_json::from_str(&input_json).unwrap_or(serde_json::Value::Null);
|
||||
YOI_PLUGIN_INSTANCE.with(|slot| {
|
||||
let mut slot = slot.borrow_mut();
|
||||
let Some(plugin) = slot.as_mut() else {
|
||||
return $crate::plugin_instance_error("plugin instance has not been started");
|
||||
};
|
||||
match plugin.handle_tool(&name, input) {
|
||||
Ok(output) => output.to_json_string(),
|
||||
Err(error) => error.into_tool_output().to_json_string(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_ingress(
|
||||
name: ::std::string::String,
|
||||
event_json: ::std::string::String,
|
||||
) -> ::std::string::String {
|
||||
let event = match serde_json::from_str::<$crate::PluginIngressEvent>(&event_json) {
|
||||
Ok(event) => event,
|
||||
Err(error) => return $crate::plugin_instance_error(error.to_string()),
|
||||
};
|
||||
YOI_PLUGIN_INSTANCE.with(|slot| {
|
||||
let mut slot = slot.borrow_mut();
|
||||
let Some(plugin) = slot.as_mut() else {
|
||||
return $crate::plugin_instance_error("plugin instance has not been started");
|
||||
};
|
||||
match plugin.handle_ingress(&name, event) {
|
||||
Ok(output) => serde_json::to_string(&output)
|
||||
.unwrap_or_else(|error| $crate::plugin_instance_error(error.to_string())),
|
||||
Err(error) => $crate::plugin_instance_error(error.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn status() -> ::std::string::String {
|
||||
YOI_PLUGIN_INSTANCE.with(|slot| {
|
||||
let slot = slot.borrow();
|
||||
let Some(plugin) = slot.as_ref() else {
|
||||
return $crate::plugin_instance_error("plugin instance has not been started");
|
||||
};
|
||||
match plugin.status() {
|
||||
Ok(status) => $crate::plugin_instance_status(&status),
|
||||
Err(error) => $crate::plugin_instance_error(error.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn stop() -> ::std::string::String {
|
||||
YOI_PLUGIN_INSTANCE.with(|slot| {
|
||||
let mut slot = slot.borrow_mut();
|
||||
let Some(plugin) = slot.as_mut() else {
|
||||
return $crate::plugin_instance_error("plugin instance has not been started");
|
||||
};
|
||||
match plugin.stop() {
|
||||
Ok(status) => {
|
||||
let output = $crate::plugin_instance_status(&status);
|
||||
*slot = None;
|
||||
output
|
||||
}
|
||||
Err(error) => $crate::plugin_instance_error(error.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export!($adapter);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5375,8 +5375,6 @@ permission = "read"
|
|||
runtime: None,
|
||||
hooks: vec![],
|
||||
tools: vec![],
|
||||
services: vec![],
|
||||
ingresses: vec![],
|
||||
permissions: vec![],
|
||||
},
|
||||
enabled_surfaces: vec![manifest::plugin::PluginSurface::Hook],
|
||||
|
|
|
|||
|
|
@ -2725,7 +2725,7 @@ mod completion_flow_tests {
|
|||
let _ = app.refresh_completion();
|
||||
app.completion.as_mut().unwrap().entries = vec![
|
||||
CompletionEntry {
|
||||
value: "crates/client".into(),
|
||||
value: "crates/daemon".into(),
|
||||
is_dir: true,
|
||||
},
|
||||
CompletionEntry {
|
||||
|
|
@ -2810,7 +2810,7 @@ mod completion_flow_tests {
|
|||
}
|
||||
let _ = app.refresh_completion();
|
||||
app.completion.as_mut().unwrap().entries = vec![CompletionEntry {
|
||||
value: "crates/client".into(),
|
||||
value: "crates/daemon".into(),
|
||||
is_dir: true,
|
||||
}];
|
||||
assert!(!app.chipify_completion_if_exact_match());
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
mod mcp_cli;
|
||||
mod memory_lint;
|
||||
mod objective_cli;
|
||||
mod plugin_cli;
|
||||
|
|
@ -19,7 +18,6 @@ enum Mode {
|
|||
Help,
|
||||
MemoryLintHelp,
|
||||
MemoryLint(LintCliOptions),
|
||||
Mcp(mcp_cli::McpCliCommand),
|
||||
Plugin(plugin_cli::PluginCliCommand),
|
||||
Objective(objective_cli::ObjectiveCli),
|
||||
Session(session_cli::SessionCli),
|
||||
|
|
@ -72,13 +70,6 @@ async fn main() -> ExitCode {
|
|||
ExitCode::FAILURE
|
||||
}
|
||||
},
|
||||
Mode::Mcp(command) => match mcp_cli::run(command) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
eprintln!("yoi mcp: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
},
|
||||
Mode::Plugin(command) => match plugin_cli::run(command) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
|
|
@ -195,10 +186,6 @@ fn parse_args_slice(args: &[String]) -> Result<Mode, ParseError> {
|
|||
let plugin_cli = parse_plugin_args(&args[1..])?;
|
||||
return Ok(Mode::Plugin(plugin_cli));
|
||||
}
|
||||
"mcp" => {
|
||||
let mcp_cli = parse_mcp_args(&args[1..])?;
|
||||
return Ok(Mode::Mcp(mcp_cli));
|
||||
}
|
||||
"panel" => {
|
||||
return Ok(Mode::Tui {
|
||||
mode: LaunchMode::Panel,
|
||||
|
|
@ -606,147 +593,6 @@ fn plugin_usage() -> &'static str {
|
|||
"usage: yoi plugin new rust-component-tool <path-or-name> [--json]\n yoi plugin check <path-or-package> [--json]\n yoi plugin pack <path> [--output <file>] [--json]\n yoi plugin list [--workspace PATH] [--profile REF] [--json]\n yoi plugin show <ref> [--workspace PATH] [--profile REF] [--json]"
|
||||
}
|
||||
|
||||
fn parse_mcp_args(args: &[String]) -> Result<mcp_cli::McpCliCommand, ParseError> {
|
||||
let Some((subcommand, rest)) = args.split_first() else {
|
||||
return Err(ParseError(
|
||||
"yoi mcp requires `list`, `show <server>`, `tools [server]`, `resources [server]`, or `prompts [server]`".to_string(),
|
||||
));
|
||||
};
|
||||
match subcommand.as_str() {
|
||||
"list" => {
|
||||
let (mcp_args, positional) = parse_mcp_common_args(rest)?;
|
||||
if !positional.is_empty() {
|
||||
return Err(ParseError(
|
||||
"yoi mcp list does not accept positional arguments".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(mcp_cli::McpCliCommand::List(mcp_args))
|
||||
}
|
||||
"show" => {
|
||||
let (mcp_args, positional) = parse_mcp_common_args(rest)?;
|
||||
match positional.as_slice() {
|
||||
[server] => Ok(mcp_cli::McpCliCommand::Show {
|
||||
server: server.clone(),
|
||||
args: mcp_args,
|
||||
}),
|
||||
[] => Err(ParseError(
|
||||
"yoi mcp show requires a server name".to_string(),
|
||||
)),
|
||||
_ => Err(ParseError(
|
||||
"yoi mcp show accepts exactly one server name".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
"tools" => {
|
||||
let (mcp_args, positional) = parse_mcp_common_args(rest)?;
|
||||
match positional.as_slice() {
|
||||
[] => Ok(mcp_cli::McpCliCommand::Tools {
|
||||
server: None,
|
||||
args: mcp_args,
|
||||
}),
|
||||
[server] => Ok(mcp_cli::McpCliCommand::Tools {
|
||||
server: Some(server.clone()),
|
||||
args: mcp_args,
|
||||
}),
|
||||
_ => Err(ParseError(
|
||||
"yoi mcp tools accepts at most one server name".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
"resources" => {
|
||||
let (mcp_args, positional) = parse_mcp_common_args(rest)?;
|
||||
match positional.as_slice() {
|
||||
[] => Ok(mcp_cli::McpCliCommand::Resources {
|
||||
server: None,
|
||||
args: mcp_args,
|
||||
}),
|
||||
[server] => Ok(mcp_cli::McpCliCommand::Resources {
|
||||
server: Some(server.clone()),
|
||||
args: mcp_args,
|
||||
}),
|
||||
_ => Err(ParseError(
|
||||
"yoi mcp resources accepts at most one server name".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
"prompts" => {
|
||||
let (mcp_args, positional) = parse_mcp_common_args(rest)?;
|
||||
match positional.as_slice() {
|
||||
[] => Ok(mcp_cli::McpCliCommand::Prompts {
|
||||
server: None,
|
||||
args: mcp_args,
|
||||
}),
|
||||
[server] => Ok(mcp_cli::McpCliCommand::Prompts {
|
||||
server: Some(server.clone()),
|
||||
args: mcp_args,
|
||||
}),
|
||||
_ => Err(ParseError(
|
||||
"yoi mcp prompts accepts at most one server name".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
"--help" | "-h" => Err(ParseError(mcp_usage().to_string())),
|
||||
other => Err(ParseError(format!("unknown yoi mcp command: {other}"))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_mcp_common_args(
|
||||
args: &[String],
|
||||
) -> Result<(mcp_cli::McpCliArgs, Vec<String>), ParseError> {
|
||||
let mut mcp_args = mcp_cli::McpCliArgs::default();
|
||||
let mut positional = Vec::new();
|
||||
let mut index = 0;
|
||||
while index < args.len() {
|
||||
let arg = &args[index];
|
||||
if arg == "--json" {
|
||||
mcp_args.json = true;
|
||||
index += 1;
|
||||
} else if arg == "--workspace" {
|
||||
let value = args
|
||||
.get(index + 1)
|
||||
.ok_or_else(|| ParseError("--workspace requires a value".to_string()))?;
|
||||
if value.starts_with('-') {
|
||||
return Err(ParseError("--workspace requires a value".to_string()));
|
||||
}
|
||||
mcp_args.workspace = Some(PathBuf::from(value));
|
||||
index += 2;
|
||||
} else if let Some(value) = arg.strip_prefix("--workspace=") {
|
||||
if value.is_empty() {
|
||||
return Err(ParseError("--workspace requires a value".to_string()));
|
||||
}
|
||||
mcp_args.workspace = Some(PathBuf::from(value));
|
||||
index += 1;
|
||||
} else if arg == "--profile" {
|
||||
let value = args
|
||||
.get(index + 1)
|
||||
.ok_or_else(|| ParseError("--profile requires a value".to_string()))?;
|
||||
if value.starts_with('-') {
|
||||
return Err(ParseError("--profile requires a value".to_string()));
|
||||
}
|
||||
mcp_args.profile = Some(value.clone());
|
||||
index += 2;
|
||||
} else if let Some(value) = arg.strip_prefix("--profile=") {
|
||||
if value.is_empty() {
|
||||
return Err(ParseError("--profile requires a value".to_string()));
|
||||
}
|
||||
mcp_args.profile = Some(value.to_string());
|
||||
index += 1;
|
||||
} else if arg == "--help" || arg == "-h" {
|
||||
return Err(ParseError(mcp_usage().to_string()));
|
||||
} else if arg.starts_with('-') {
|
||||
return Err(ParseError(format!("unknown yoi mcp argument: {arg}")));
|
||||
} else {
|
||||
positional.push(arg.clone());
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
Ok((mcp_args, positional))
|
||||
}
|
||||
|
||||
fn mcp_usage() -> &'static str {
|
||||
"usage: yoi mcp list [--workspace PATH] [--profile REF] [--json]\n yoi mcp show <server> [--workspace PATH] [--profile REF] [--json]\n yoi mcp tools [server] [--workspace PATH] [--profile REF] [--json]\n yoi mcp resources [server] [--workspace PATH] [--profile REF] [--json]\n yoi mcp prompts [server] [--workspace PATH] [--profile REF] [--json]"
|
||||
}
|
||||
|
||||
fn parse_panel_workspace(args: &[String]) -> Result<PathBuf, ParseError> {
|
||||
match args {
|
||||
[] => std::env::current_dir()
|
||||
|
|
@ -777,7 +623,7 @@ fn parse_session_id(value: &str) -> Result<SegmentId, ParseError> {
|
|||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"yoi\n\nUsage:\n yoi [OPTIONS] [POD_NAME]\n yoi panel [--workspace <PATH>]\n yoi keys\n yoi setup-model\n yoi pod [POD_OPTIONS]\n yoi objective <COMMAND> [OPTIONS]\n yoi session analyze <SESSION_JSONL_PATH> --json\n yoi ticket <COMMAND> [OPTIONS]\n yoi plugin new rust-component-tool <PATH> [--json]\n yoi plugin check <PATH_OR_PACKAGE> [--json]\n yoi plugin pack <PATH> [--output <FILE>] [--json]\n yoi plugin list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi plugin show <REF> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi mcp list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi mcp show <SERVER> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi mcp tools|resources|prompts [SERVER] [--workspace <PATH>] [--profile <REF>] [--json]\n yoi memory lint [OPTIONS]\n\nSurfaces:\n Console Single-Pod chat/client surface (default, --pod, --resume)\n Dashboard Workspace cockpit/action surface (yoi panel)\n TUI Terminal UI implementation umbrella for Console and Dashboard\n\nOptions:\n -r, --resume Open the Pod Console picker and resume/attach a Pod\n --workspace <PATH> Runtime workspace root (defaults to cwd)\n --pod <NAME> Open the Pod Console by name (attach/restore/create)\n --socket <PATH> Attach a Pod Console to a specific socket with --pod\n --session <UUID> Resume a specific session segment in the Pod Console\n --profile <REF> Select a reusable Profile recipe\n -h, --help Print help\n"
|
||||
"yoi\n\nUsage:\n yoi [OPTIONS] [POD_NAME]\n yoi panel [--workspace <PATH>]\n yoi keys\n yoi setup-model\n yoi pod [POD_OPTIONS]\n yoi objective <COMMAND> [OPTIONS]\n yoi session analyze <SESSION_JSONL_PATH> --json\n yoi ticket <COMMAND> [OPTIONS]\n yoi plugin new rust-component-tool <PATH> [--json]\n yoi plugin check <PATH_OR_PACKAGE> [--json]\n yoi plugin pack <PATH> [--output <FILE>] [--json]\n yoi plugin list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi plugin show <REF> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi memory lint [OPTIONS]\n\nSurfaces:\n Console Single-Pod chat/client surface (default, --pod, --resume)\n Dashboard Workspace cockpit/action surface (yoi panel)\n TUI Terminal UI implementation umbrella for Console and Dashboard\n\nOptions:\n -r, --resume Open the Pod Console picker and resume/attach a Pod\n --workspace <PATH> Runtime workspace root (defaults to cwd)\n --pod <NAME> Open the Pod Console by name (attach/restore/create)\n --socket <PATH> Attach a Pod Console to a specific socket with --pod\n --session <UUID> Resume a specific session segment in the Pod Console\n --profile <REF> Select a reusable Profile recipe\n -h, --help Print help\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -968,57 +814,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mcp_commands() {
|
||||
match parse_args_from(["mcp", "list", "--workspace=/tmp/ws", "--json"]).unwrap() {
|
||||
Mode::Mcp(mcp_cli::McpCliCommand::List(options)) => {
|
||||
assert_eq!(options.workspace, Some(PathBuf::from("/tmp/ws")));
|
||||
assert!(options.json);
|
||||
}
|
||||
_ => panic!("expected MCP list mode"),
|
||||
}
|
||||
|
||||
match parse_args_from(["mcp", "show", "filesystem", "--profile", "project:mcp"]).unwrap() {
|
||||
Mode::Mcp(mcp_cli::McpCliCommand::Show { server, args }) => {
|
||||
assert_eq!(server, "filesystem");
|
||||
assert_eq!(args.profile.as_deref(), Some("project:mcp"));
|
||||
}
|
||||
_ => panic!("expected MCP show mode"),
|
||||
}
|
||||
|
||||
match parse_args_from(["mcp", "tools", "filesystem"]).unwrap() {
|
||||
Mode::Mcp(mcp_cli::McpCliCommand::Tools { server, .. }) => {
|
||||
assert_eq!(server.as_deref(), Some("filesystem"));
|
||||
}
|
||||
_ => panic!("expected MCP tools mode"),
|
||||
}
|
||||
|
||||
match parse_args_from(["mcp", "resources"]).unwrap() {
|
||||
Mode::Mcp(mcp_cli::McpCliCommand::Resources { server, .. }) => {
|
||||
assert!(server.is_none());
|
||||
}
|
||||
_ => panic!("expected MCP resources mode"),
|
||||
}
|
||||
|
||||
match parse_args_from(["mcp", "prompts", "filesystem"]).unwrap() {
|
||||
Mode::Mcp(mcp_cli::McpCliCommand::Prompts { server, .. }) => {
|
||||
assert_eq!(server.as_deref(), Some("filesystem"));
|
||||
}
|
||||
_ => panic!("expected MCP prompts mode"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mcp_rejects_usage_errors() {
|
||||
let err = parse_args_from(["mcp", "show"]).unwrap_err();
|
||||
assert_eq!(err.to_string(), "yoi mcp show requires a server name");
|
||||
let err = parse_args_from(["mcp", "list", "extra"]).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"yoi mcp list does not accept positional arguments"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_memory_lint_rejects_usage_errors() {
|
||||
let err = parse_args_from(["memory", "lint", "--workspace"]).unwrap_err();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -329,24 +329,6 @@ fn static_inspection_diagnostics(
|
|||
});
|
||||
}
|
||||
}
|
||||
for service in &inspection.services {
|
||||
if let Some(message) = &service.diagnostic {
|
||||
diagnostics.push(PluginDiagnosticReport {
|
||||
kind: "grant".to_string(),
|
||||
phase: "resolution".to_string(),
|
||||
message: bound_text(format!("service `{}`: {message}", service.name)),
|
||||
});
|
||||
}
|
||||
}
|
||||
for ingress in &inspection.ingresses {
|
||||
if let Some(message) = &ingress.diagnostic {
|
||||
diagnostics.push(PluginDiagnosticReport {
|
||||
kind: "grant".to_string(),
|
||||
phase: "resolution".to_string(),
|
||||
message: bound_text(format!("ingress `{}`: {message}", ingress.name)),
|
||||
});
|
||||
}
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
|
|
@ -1090,18 +1072,6 @@ fn fill_resolved(builder: &mut ItemBuilder, resolved: &ResolvedPlugin) {
|
|||
.iter()
|
||||
.filter_map(|tool| tool.diagnostic.as_ref()),
|
||||
)
|
||||
.chain(
|
||||
static_runtime
|
||||
.services
|
||||
.iter()
|
||||
.filter_map(|service| service.diagnostic.as_ref()),
|
||||
)
|
||||
.chain(
|
||||
static_runtime
|
||||
.ingresses
|
||||
.iter()
|
||||
.filter_map(|ingress| ingress.diagnostic.as_ref()),
|
||||
)
|
||||
{
|
||||
builder.diagnostics.push(DiagnosticSummary {
|
||||
kind: "static_eligibility".to_string(),
|
||||
|
|
@ -1503,58 +1473,6 @@ mod tests {
|
|||
assert!(show.contains("configured_grants: surfaces.tool, tool.Echo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_only_enablement_ignores_unselected_tool_static_grants() {
|
||||
let dir = tempdir().unwrap();
|
||||
let workspace = dir.path();
|
||||
let digest = write_mixed_tool_service_package(workspace, "mixed");
|
||||
let mut config = PluginConfig::default();
|
||||
config.enabled.push(PluginEnablementConfig {
|
||||
id: "project:mixed".to_string(),
|
||||
digest: Some(digest.clone()),
|
||||
version: Some(PluginExactVersion("0.1.0".to_string())),
|
||||
surfaces: vec![PluginSurface::Service],
|
||||
grants: PluginGrantConfig {
|
||||
id: Some("project:mixed".to_string()),
|
||||
version: Some(PluginExactVersion("0.1.0".to_string())),
|
||||
digest: Some(digest),
|
||||
permissions: vec![
|
||||
PluginPermission::surface(PluginSurface::Service),
|
||||
PluginPermission::service("svc"),
|
||||
],
|
||||
https: Vec::new(),
|
||||
fs: Vec::new(),
|
||||
},
|
||||
config: None,
|
||||
});
|
||||
|
||||
let snapshot = inspect_snapshot(workspace, &config);
|
||||
let item = select_item(&snapshot, "project:mixed").unwrap();
|
||||
|
||||
assert_eq!(item.status, "active");
|
||||
assert!(item.static_eligible);
|
||||
assert_eq!(item.enabled_surfaces, vec!["service"]);
|
||||
assert!(
|
||||
item.tools.is_empty(),
|
||||
"unselected Tool must not be reported"
|
||||
);
|
||||
assert!(
|
||||
item.diagnostics
|
||||
.iter()
|
||||
.all(|diagnostic| !diagnostic.message.contains("tool.Echo")),
|
||||
"unselected Tool grant diagnostics must not affect service-only enablement: {:#?}",
|
||||
item.diagnostics
|
||||
);
|
||||
|
||||
let show_json = serde_json::to_value(item).unwrap();
|
||||
assert_eq!(show_json["status"], "active");
|
||||
assert_eq!(
|
||||
show_json["enabled_surfaces"],
|
||||
serde_json::json!(["service"])
|
||||
);
|
||||
assert_eq!(show_json["tools"], serde_json::json!([]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn human_list_uses_required_status_vocabulary() {
|
||||
let dir = tempdir().unwrap();
|
||||
|
|
@ -2162,61 +2080,6 @@ mod tests {
|
|||
assert!(error.len() < 160);
|
||||
}
|
||||
|
||||
fn write_mixed_tool_service_package(workspace: &Path, id: &str) -> String {
|
||||
let package_dir = workspace.join(".yoi/plugins");
|
||||
fs::create_dir_all(&package_dir).unwrap();
|
||||
let package = package_dir.join(format!("{id}.yoi-plugin"));
|
||||
let manifest = format!(
|
||||
r#"schema_version = 1
|
||||
id = "{id}"
|
||||
name = "{id}"
|
||||
version = "0.1.0"
|
||||
description = "mixed surface package"
|
||||
surfaces = ["tool", "service"]
|
||||
permissions = [
|
||||
{{ kind = "surface", surface = "tool" }},
|
||||
{{ kind = "tool", name = "Echo" }},
|
||||
{{ kind = "surface", surface = "service" }},
|
||||
{{ kind = "service", name = "svc" }},
|
||||
]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
world = "yoi:plugin/instance@1.0.0"
|
||||
component = "plugin.component.wasm"
|
||||
|
||||
[[tools]]
|
||||
name = "Echo"
|
||||
description = "unselected tool"
|
||||
input_schema = {{ type = "object" }}
|
||||
|
||||
[[services]]
|
||||
name = "svc"
|
||||
description = "selected service"
|
||||
lifecycle = "host-managed"
|
||||
"#,
|
||||
);
|
||||
write_stored_zip(
|
||||
&package,
|
||||
&[
|
||||
("plugin.toml", manifest.as_bytes()),
|
||||
("plugin.component.wasm", b"placeholder component bytes"),
|
||||
],
|
||||
);
|
||||
let discovery = discover_plugins(&PluginDiscoveryOptions {
|
||||
workspace_root: workspace.to_path_buf(),
|
||||
user_data_home: None,
|
||||
limits: PluginDiscoveryLimits::default(),
|
||||
});
|
||||
discovery
|
||||
.packages
|
||||
.iter()
|
||||
.find(|package| package.identity.local_id == id)
|
||||
.unwrap()
|
||||
.digest
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn inspect_snapshot(workspace: &Path, config: &PluginConfig) -> PluginInspectionSnapshot {
|
||||
let discovery = discover_plugins(&PluginDiscoveryOptions {
|
||||
workspace_root: workspace.to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -179,26 +179,3 @@ semantics while moving package authors onto WIT/canonical ABI bindings.
|
|||
Structured WIT records for Tool requests/responses/errors and host HTTPS/FS
|
||||
payloads are deferred to a follow-up API-design step rather than accidentally
|
||||
omitted.
|
||||
|
||||
## Instance lifecycle surface
|
||||
|
||||
The first instance-capable world is `yoi:plugin/instance@1.0.0`. It moves
|
||||
runtime ownership from per-Tool artifact execution to a host-managed
|
||||
`PluginInstance`. The same instance handles Tool, Service, and Ingress surfaces,
|
||||
so Plugin state/config/diagnostics can be shared without bypassing Yoi's normal
|
||||
authority model.
|
||||
|
||||
Important boundaries:
|
||||
|
||||
- Tool calls still enter through `ToolRegistry` and return ordinary `ToolOutput`
|
||||
that is visible in the Worker history path.
|
||||
- Service and Ingress grants are separate from Tool grants. Sharing an instance
|
||||
does not authorize a surface that lacks its own `surface.*` and per-surface
|
||||
permission/grant.
|
||||
- Ingress delivery accepts bounded typed untrusted events and returns explicit
|
||||
JSON to the host. It does not call model Tools or mutate LLM context/history.
|
||||
- Legacy raw-wasm and `yoi:plugin/tool@1.0.0` component packages are adapted
|
||||
behind `PluginInstanceRegistry` for compatibility rather than executed through
|
||||
a separate authority path.
|
||||
- Host APIs such as `https` and `fs` remain independently grant-gated and still
|
||||
reject ambient filesystem/network authority.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ rustPlatform.buildRustPackage rec {
|
|||
filter = sourceFilter;
|
||||
};
|
||||
|
||||
cargoHash = "sha256-GUqhvq+JhJokk1R4VVeVz5cZe/6oSrVMyKjcltZEWqE=";
|
||||
cargoHash = "sha256-rvsjn4BBxd9vt4nytPgUh4l/OQCRpqHbUR4jHoH589U=";
|
||||
|
||||
depsExtraArgs = {
|
||||
# Older fetchCargoVendor utilities used crates.io's API download endpoint,
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
[workspace]
|
||||
|
||||
[package]
|
||||
name = "example-yoi-instance-plugin"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
yoi-plugin-pdk = { path = "../../../../crates/plugin-pdk" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Yoi instance Plugin template
|
||||
|
||||
This template targets `yoi:plugin/instance@1.0.0`. The host creates one
|
||||
`PluginInstance` for the package; Tool, Service, and Ingress surfaces share that
|
||||
instance state while each surface keeps separate permissions/grants.
|
||||
|
||||
Tools still run only through ordinary model/user-initiated Tool calls. Ingress
|
||||
handlers receive bounded typed untrusted events and must return explicit JSON
|
||||
for host-mediated visible/durable paths.
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# Build with:
|
||||
# cargo component build --release
|
||||
# cp target/wasm32-wasip1/release/example_yoi_instance_plugin.wasm plugin.component.wasm
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
schema_version = 1
|
||||
id = "example.rust_instance_plugin"
|
||||
name = "Rust Instance Plugin Template"
|
||||
version = "0.1.0"
|
||||
description = "Example instance-oriented Yoi Plugin with shared Tool/Ingress state."
|
||||
surfaces = ["tool", "service", "ingress"]
|
||||
permissions = [
|
||||
{ kind = "surface", surface = "tool" },
|
||||
{ kind = "tool", name = "example_instance_tool" },
|
||||
{ kind = "surface", surface = "service" },
|
||||
{ kind = "service", name = "example_instance_service" },
|
||||
{ kind = "surface", surface = "ingress" },
|
||||
{ kind = "ingress", name = "example_instance_ingress" },
|
||||
]
|
||||
|
||||
[runtime]
|
||||
kind = "wasm-component"
|
||||
world = "yoi:plugin/instance@1.0.0"
|
||||
component = "plugin.component.wasm"
|
||||
|
||||
[[tools]]
|
||||
name = "example_instance_tool"
|
||||
description = "Return the input and increment shared instance state."
|
||||
input_schema = { type = "object" }
|
||||
|
||||
[[services]]
|
||||
name = "example_instance_service"
|
||||
description = "Reports shared plugin instance lifecycle status."
|
||||
lifecycle = "host-managed"
|
||||
|
||||
[[ingresses]]
|
||||
name = "example_instance_ingress"
|
||||
description = "Accepts bounded in-process ingress events."
|
||||
event_kinds = ["example"]
|
||||
input_schema = { type = "object" }
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
use serde_json::{json, Value};
|
||||
use yoi_plugin_pdk::wit_bindgen;
|
||||
use yoi_plugin_pdk::{export_plugin_instance, Plugin, PluginIngressEvent, PluginStatus, ToolOutput};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
world: "instance",
|
||||
path: "../../../../resources/plugin/wit",
|
||||
generate_all,
|
||||
runtime_path: "yoi_plugin_pdk::wit_bindgen::rt",
|
||||
});
|
||||
|
||||
struct ExamplePlugin {
|
||||
calls: u64,
|
||||
}
|
||||
|
||||
impl Plugin for ExamplePlugin {
|
||||
fn start(_config: Value) -> yoi_plugin_pdk::Result<Self> {
|
||||
Ok(Self { calls: 0 })
|
||||
}
|
||||
|
||||
fn handle_tool(&mut self, name: &str, input: Value) -> yoi_plugin_pdk::Result<ToolOutput> {
|
||||
self.calls += 1;
|
||||
ToolOutput::json(
|
||||
format!("{name} handled by shared instance"),
|
||||
json!({
|
||||
"tool": name,
|
||||
"calls": self.calls,
|
||||
"input": input
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_ingress(
|
||||
&mut self,
|
||||
name: &str,
|
||||
event: PluginIngressEvent,
|
||||
) -> yoi_plugin_pdk::Result<Value> {
|
||||
Ok(json!({
|
||||
"ingress": name,
|
||||
"kind": event.kind,
|
||||
"source": event.source,
|
||||
"calls": self.calls,
|
||||
"accepted": true
|
||||
}))
|
||||
}
|
||||
|
||||
fn status(&self) -> yoi_plugin_pdk::Result<PluginStatus> {
|
||||
Ok(PluginStatus::ready(json!({ "calls": self.calls })))
|
||||
}
|
||||
}
|
||||
|
||||
export_plugin_instance!(ExamplePluginComponent, ExamplePlugin);
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package yoi:plugin@1.0.0;
|
||||
|
||||
world instance {
|
||||
import yoi:host/https@1.0.0;
|
||||
import yoi:host/fs@1.0.0;
|
||||
|
||||
export start: func(config-json: string) -> string;
|
||||
export handle-tool: func(name: string, input-json: string) -> string;
|
||||
export handle-ingress: func(name: string, event-json: string) -> string;
|
||||
export status: func() -> string;
|
||||
export stop: func() -> string;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user