ticket: move local storage to .yoi/tickets

This commit is contained in:
2026-06-06 06:44:10 +09:00
parent 749b3f3aee
commit da94b1ec50
529 changed files with 98 additions and 156 deletions
@@ -0,0 +1,105 @@
---
id: 20260527-000002-e2e-harness
slug: e2e-harness
title: E2E テストハーネス
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:02Z
updated_at: 2026-05-27T00:00:02Z
assignee: null
legacy_ticket: tickets/e2e-harness.md
---
## Migration reference
- legacy_ticket: tickets/e2e-harness.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# E2E テストハーネス
## 背景
`CLAUDE.md:6` で明記している通り、現状「実プロセスをスポーンさせての E2E」は未設計である。crate 内 integration test は 121 ファイル / 969 ケースまで揃っているが、以下の領域は in-process では再現できず、複数チケットの完了条件が宙に浮いている。
- `pod-cli-manifest-flags`: `--manifest` / `INSOMNIA_USER_MANIFEST` / 併用 conflict など実 CLI 挙動
- `pod-persistent-state`: Pod プロセス**再起動後**の active session 復元、spawner 再起動後の `ListPods` 復元
- `pod-session-fork`: `pod_cli` から fork → 新 session で run まで通せる
- `pod-parent-turn-callback`: 実子 Pod を spawn した状態の親 history 反映
- `pod-empty-turn-rollback`: 「TUI / pod_cli いずれの経路でも」明記
- `permission-extension-point.review.md:21`: `[permissions]` を含む Pod 構築 → tool deny までの結合検証
- `llm-worker-stream-continuation`: SSE 途中切断 + 継続/中断と課金重複が無いことの確認
- `native-gui-mvp`: GUI から `pod` subprocess を起動 → socket 接続 → graceful shutdown
`crates/pod/tests/spawn_pod_test.rs` のように subprocess を `/bin/true` ですり替える擬似手法は既にあるが、これは「子 Pod が即終了する状況下での registry 書き込み」を見るためのもので、実 pod を立ち上げての protocol 往復はしていない。
## 方針
- ワークスペース直下 **`tests/e2e/`** に E2E 専用の crate を切る。E2E は単一の crate / バイナリの責務ではないため、既存 `crates/<x>/tests/` には置かない。
-`pod` バイナリは `env!("CARGO_BIN_EXE_pod")` で取得。ファイルシステムは tmpdir に閉じ、`INSOMNIA_RUNTIME_DIR` / data dir / `INSOMNIA_USER_MANIFEST` 等を env で完全 sandbox 化する。
- protocol を喋る側は **`tickets/client-crate.md` で切り出す `client` crate** を直接利用する。TUI バイナリを PTY で叩く方針は採らない(GUI MVP との整合と E2E 安定性の観点から)。
- CI 既定実行から外す。`--features e2e` か独立ジョブで opt-in。ローカルでは `cargo test -p e2e --features e2e` 相当で叩ける形にする。
## 詰めたい論点(実装前に決める)
### 1. LLM provider のスタブ手段が fixture HTTP 再生だけで充足するか
既存 `crates/llm-worker/tests/anthropic_fixtures.rs` 等は in-process loader として書かれており、HTTP サーバーとして再生する形にはなっていない。E2E では Pod プロセスが env で渡された URL に対して実 HTTP を叩く以上、**最低限「fixture を返す HTTP サーバー」** は必要。
ただし、それだけで充足するかは不明:
- **動的応答が要るシナリオ**: SSE を途中で能動的に切る (`llm-worker-stream-continuation`)、tool 呼び出しの結果に応じて分岐する応答、複数ターンに渡る会話の途中で挙動を変える、など。録画再生だけでは作りにくい。
- **provider 差**: Anthropic / OpenAI Responses / Gemini / Ollama / Codex OAuth で endpoint / 認証 / スキーマが違う。E2E で全 provider を回す必要は無いが、最低 1〜2 provider はハーネスを持たせるべきで、選定が要る。
- **OAuth 系**: Codex OAuth はトークン取得経路自体が外部依存。E2E では事前注入された token を読む形に倒すか、OAuth flow ごと canned server で模すか。
このチケットでは「fixture HTTP 再生」を出発点としつつ、**動的応答のための最小 canned server インターフェース**(テストケース側からハンドラを差し替えられる形)も同時に検討範囲に含める。両方が無いと上のシナリオが書けない。
### 2. provider URL の差し替え経路
各 provider の base URL を env で上書きできる前提が、現コードに揃っているか確認・整備する必要がある。揃っていなければ別チケットに切り出すか、本チケット内で minimal に対応するか決める。
### 3. fixture 形式
既存の in-process fixture (`tests/*_fixtures.rs`) と HTTP 再生用 fixture を同じソースから作るか、別管理にするか。共通化できるなら record/replay 経路を整備する。
### 4. 並列実行と env 干渉
`spawn_pod_test.rs` は env mutex で直列化している。E2E でも env (`INSOMNIA_*`)・runtime dir・socket path に依存する以上、テスト並列度の方針を決める(`--test-threads=1`、test-per-process、または env を引数にハンドオフして mutex 不要にする)。
### 5. 失敗時の診断
実プロセスが絡むためスタックトレースだけでは原因特定しにくい。pod の stderr / stdout、session log、runtime dir の中身をテスト失敗時に dump する仕組みを最初に入れておく。
## 要件
- `tests/e2e/` 以下に E2E 用 crate(仮称 `e2e`)が存在し、`Cargo.toml``[features] e2e = []` で gate されている。
- `cargo test -p e2e --features e2e` で実 `pod` バイナリを spawn し、protocol 経由で 1 シナリオ(最小: spawn → 1 turn 実行 → graceful shutdown)が通る。
- LLM provider のスタブが少なくとも 1 provider 分動き、上の最小シナリオが本物の HTTP 越しに完結する。
- env / tmpdir / socket path が tmpdir 内に閉じ、テスト間の干渉が無い。
- テスト失敗時に pod プロセスの stderr / 関連ファイルが artefact として確認できる。
- CI 既定パス (`cargo test --workspace`) では E2E が走らない。opt-in jobs でだけ走る。
- 上の論点 1〜5 が文書化されている(チケット内 or `docs/` 配下のいずれか)。
## 完了条件
- 上記要件を満たすハーネスが入り、最小シナリオ 1 本が通る。
- 後続シナリオ(permission deny / cli-manifest-flags / spawn 親子 / resume / fork / stream-continuation)を**書く側の手順書**が提示されている(fixture 追加方法、シナリオ crate の追加方法、env のお作法)。
- 個別シナリオの実装は本チケットに含めない。後続チケットで切る。
## 範囲外
- 個別 E2E シナリオの実装(permission deny / cli flags / spawn / resume / fork / stream-continuation)。それぞれ後続チケット。
- 全 provider 分の HTTP スタブ(最初は 1 provider に絞る)。
- TUI バイナリを PTY で操作する経路。
- GUI バイナリの E2E`tickets/native-gui-mvp.md` 完了後に別途)。
- E2E を CI 既定で走らせる切替。
## 依存 / 関連
- `tickets/client-crate.md`protocol を喋る client crate を切り出す。E2E はここに依存して書く)
- `tickets/llm-worker-stream-continuation.md`(動的応答 canned server を必要とする最初のシナリオ)
- `tickets/permission-extension-point.review.md`(最初に書きたいシナリオ)
- `crates/pod/tests/spawn_pod_test.rs`(env mutex 等の流儀を流用)
- `crates/llm-worker/tests/*_fixtures.rs`fixture 資産の出発点)
- `CLAUDE.md:6`E2E 未設計の宣言)
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:02Z -->
## Migrated
Migrated from tickets/e2e-harness.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,92 @@
---
id: 20260527-000003-internal-worker-workflow
slug: internal-worker-workflow
title: 内部 Worker / 内部 Pod の Workflow 化
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:03Z
updated_at: 2026-05-27T00:00:03Z
assignee: null
legacy_ticket: tickets/internal-worker-workflow.md
---
## Migration reference
- legacy_ticket: tickets/internal-worker-workflow.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# 内部 Worker / 内部 Pod の Workflow 化
## 背景
INSOMNIA が内部で固定 prompt を持って disposable Worker / 専用 Pod を立ち上げている経路がいくつかある:
- extract 活動抽出(`crates/memory/src/extract/prompt.rs::EXTRACT_SYSTEM_PROMPT`
- consolidation 統合 + 整理(`tickets/memory-consolidation.md`、本チケット時点では実装中 / 直前)
- Compact`PromptCatalog::compact_system`
これらは実装内 `&str` 定数や `PromptCatalog` の overlay で管理されており、prompt の調整や運用カスタマイズが「コード変更 + 再ビルド」を要する。一方、ユーザー向け `/<slug>` Workflow`tickets/workflow.md`)は `<workspace_root>/.insomnia/workflow/<slug>.md` に住み、frontmatter + Markdown 本文 + `requires` Knowledge inject を持つ宣言形式で運用できる。
両者を寄せ、内部 Worker / 内部 Pod の prompt + ツール surface + Knowledge 依存を **Workflow と同一仕様で記述** できる経路を用意する。これにより:
- 内部 prompt の運用調整が workspace 側でできる(コード変更不要)
- consolidation の prompt 案 (`docs/plan/memory-prompts.md`) を workspace に直接 ingest できる
- 将来 consolidation を独立 Pod に引き上げる際も、Workflow を submit する形に揃えられる
## 要件
### Workflow の役割拡張
`tickets/workflow.md` の Workflow 仕様は「ユーザーが `/<slug>` で submit する制約付き作業」だが、本チケットでは **内部トリガー(Pod 内部の状態遷移)から呼び出される Workflow** を一級扱いに広げる。
- 同じファイル形式(`.insomnia/workflow/<slug>.md`)、同じ frontmatter / Linter
- `user_invocable: false``/<slug>` 経路から見えなくする
- `model_invokation` は通常 Pod 用の system prompt 注入仕様のまま(内部 Workflow は通常 OFF
- 内部 Workflow を識別するキー(例: `internal_role`)と、必要なツール surface を表明する手段を frontmatter に追加する。具体 schema は実装で詰める
### 内部呼び出し経路
Pod 側の既存トリガー(extract post-run / consolidation staging 閾値 / Compact 閾値 等)は固定 `&str` の代わりに Workflow loader 経由で:
1. 内部識別キーで該当 Workflow を解決(衝突時は workspace 上書き優先、なければ insomnia bundled default
2. `requires` Knowledge を本文の前に inject
3. Workflow 本文を sub-Worker / sub-Pod の prompt として渡す(system prompt 扱いか初回 submit 扱いかは内部用途で固定し、role ごとに揃える)
4. 既存のツール登録ロジックは Workflow が表明したツール surface に従う
### Bundled defaults
ユーザー workspace に該当 Workflow が無い場合に備え、insomnia 同梱の default Workflow を読む層を `PromptCatalog` の overlay と整合する形で持つ。
- 既存の Pod prompt 4 層 overlaybuiltin / user / workspace / pod-pack)と同じ優先順
- bundled default の物理配置は実装で決める
### 関連チケットとの順序
- `tickets/workflow.md`(ユーザー向け Workflow 本体)が先行する。本チケットはその仕様を前提に「内部呼び出し経路」を追加する側
- `tickets/memory-consolidation.md` は当面 `&str` 定数で実装してよい。本チケット完了時に Workflow 化に乗り換える
- extract / Compact も同様に role ごとに段階移行
## 範囲外
- Workflow 仕様自体の本体実装(`tickets/workflow.md`
- 内部 Workflow の自動生成(consolidation の offer 等。`docs/plan/memory.md` §Offer 経路 / 将来検討)
- 既存 `&str` 定数の物理削除タイミング(移行が完了した role ごとに削除する運用)
- `model_invokation` 注入予算の最適化(既存 Knowledge 常駐注入予算と合算する規約は `docs/plan/memory.md` 側)
## 完了条件
- 各内部 Worker / 内部 Pod(少なくとも extract / consolidation / Compact のうち、本チケット着手時点で実装済みのもの)が内部識別キー付き Workflow を解決して prompt とツール surface を組み立てる
- workspace で `.insomnia/workflow/<slug>.md` を上書きすれば内部 Worker の prompt が変わる
- workspace に該当 Workflow が無い場合、bundled default が使われる
- `user_invocable: false` の内部 Workflow は `/<slug>` 候補から除外され、ユーザーからは呼べない
- 内部 Workflow も consolidation の自動書き込み禁止対象のまま(Linter で構造的担保、`workflow.md` と整合)
- 単体テストで bundled default / workspace overlay / ツール surface 表明 + 解決 + 適用がカバーされる
## 参照
- 前提: `tickets/workflow.md`
- 最初の利用者: `tickets/memory-consolidation.md`
- 関連: `tickets/agent-skills.md`(外部 SKILL ingest 経路。本チケットの内部呼び出し経路とは別軸)
- 設計: `docs/plan/workflow.md``docs/plan/memory.md``docs/plan/memory-prompts.md`
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:03Z -->
## Migrated
Migrated from tickets/internal-worker-workflow.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,50 @@
---
id: 20260527-000006-permission-default-policy
slug: permission-default-policy
title: Permission: allow-all 既定 policy への整理
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:06Z
updated_at: 2026-05-27T00:00:06Z
assignee: null
legacy_ticket: tickets/permission-default-policy.md
---
## Migration reference
- legacy_ticket: tickets/permission-default-policy.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# Permission: allow-all 既定 policy への整理
## 背景
現在の tool permission は `[permissions]` セクションが無い場合に permission 層を無効化し、`[permissions]` がある場合だけ `default_action` を必須としている。
実行時の意味として、未指定時の挙動はほぼ `default_action = "allow"` と同じであり、`Option<ToolPermissionConfig>` による「無効」と allow-all policy が型上で分かれていることが仕様理解と実装の分岐を増やしている。
## 要件
- resolved manifest の permission は常に policy として存在する形に整理する。
- 既定 policy は allow-all とし、`default_action = "allow"` かつ rule なしと同等にする。
- manifest に `[permissions]` が無い既存ユーザー設定は従来通り全ツール実行可能にする。
- `default_action = "deny"` による allowlist 型運用と、`default_action = "allow"` + deny rule による blocklist 型運用を明確に維持する。
- merge/parse 用の partial config では、「その層が permissions に触れていない」ことを表現できるようにする。
## 方針
`PodManifest` のような resolve 後の型では `permissions: ToolPermissionConfig` を持ち、`ToolPermissionConfig::default()` を allow-all とする。
`PodManifestConfig` / partial 側では階層 manifest の merge semantics のために `Option<PermissionConfigPartial>` を残してよい。resolve 時に未指定を allow-all default policy へ畳み込む。
`[permissions]` セクションを書いた場合の `default_action` 必須制約は見直す。rule だけを書いた場合は `default_action = "allow"` と解釈できるようにするか、明示必須を維持する場合でも resolved 型上は allow-all default と矛盾しない形にする。
## 完了条件
- resolve 後の manifest から permission policy の `Option` が消えている。
- `[permissions]` 未指定時に allow-all policy が得られる。
- permission rule 評価と Pod への built-in hook 登録が、常在 policy 前提で単純化されている。
- manifest resolve / merge / permission hook のテストが新しい既定値をカバーしている。
- docs の `[permissions]` 説明が allow-all 既定であることを明記している。
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:06Z -->
## Migrated
Migrated from tickets/permission-default-policy.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,75 @@
---
id: 20260527-000009-pod-session-fork
slug: pod-session-fork
title: Pod: 任意ターンからの Fork(複数ターン巻き戻し)
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:09Z
updated_at: 2026-05-27T00:00:09Z
assignee: null
legacy_ticket: tickets/pod-session-fork.md
---
## Migration reference
- legacy_ticket: tickets/pod-session-fork.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# Pod: 任意ターンからの Fork(複数ターン巻き戻し)
## 背景
`tickets/pod-empty-turn-rollback.md` は「直近 Submit が AI 応答ゼロのまま中断された」極めて狭いケースだけを自動で巻き戻す簡易フォーム。それを超える「3 ターン前から別の方針でやり直したい」「ある分岐は捨てて別ルートを試したい」といった **複数ターン巻き戻し** は、過去ターン境界からの Fork として実装する。
session_store には既に primitive が揃っている:
- `session::fork(state)` — 現状から新 session_id へ分岐(`crates/session-store/src/session.rs:400`
- `session::fork_at(source_id, at_hash)` — 既存セッションログ上の任意 entry hash から分岐(同 :424
- `SessionOrigin { session_id, at_hash }``SessionStart.forked_from` に出自を記録
未着手なのは Pod / protocol / クライアントへの露出と、ターン境界 ↔ entry hash の対応付け。
## 要件
- Pod に「現セッションから Fork して新セッションへ切り替える」操作を追加。Fork 起点はターン境界で指定する:
- protocol に新 Method(仮: `Method::Fork { from: ForkPoint }`)を追加
- `ForkPoint` は最低限「ターン番号」「entry hash」のいずれかで起点を指す
- ターン番号 → entry hash の解決は Pod / session_store 側で行う(`save_turn_end` のログ entry を境界として使うのが自然)
- Fork 後の Pod 状態:
- 新 session_id を active に切り替え、worker.history を fork 起点までの内容で再構築
- 元セッションは破壊されない(後から switch back 可能な前提を残す)
- 走行中(Running / Paused)状態での Fork は拒否し、Idle 限定
- Fork ツリーが追跡可能であること:
- `forked_from` chain が session_store のログから辿れる(既存挙動の確認込み)
- クライアントから「このセッションの祖先 / 子孫」を引ける API(最低限、`SessionOrigin` を読める形)
- pod_cli / TUI からの呼び出しインターフェースの設計:
- 本チケットで protocol 上の Method は確定させる
- pod_cli の引数仕様もここに含める(最低限 `pod fork --turn N` 程度)
- TUI 側の UX(ターンを選択して fork する操作)は別チケット
- セッション切り替え後の `runtime_dir` の扱いを decide:
- 1 つの runtime に対して active session が切り替わる形
- セッションごとに別 runtime を持つ形
- のどちらが今の構成と整合するか調査の上で決定
## 完了条件
- `Method::Fork` で過去ターン起点の新セッションが作成され、そこから `Method::Run` で続行できる
- 元セッションが fork 後も独立に存在し、別 Pod プロセスから resume できる(破壊されていない)
- Fork ツリーが session_store のログから機械的に辿れる
- pod_cli から fork → 新セッションでの run まで通せる
- `pod-empty-turn-rollback` の自動巻き戻しと共存(自動巻き戻しは本機能を使わずに従来通りの直接 truncate 方式で良い、と決めるならその根拠も明記)
## 範囲外
- Fork ツリーの可視化 UI(TUI / GUI)— 別チケット
- TUI 上での「ターンを選んで fork」UX — 別チケット
- 異なる Fork 間でのマージ
- 過去ターンの物理削除型リベース(fork は常に非破壊)
- 自動 GC / 古い fork の整理
## 依存 / 関連
- `tickets/pod-empty-turn-rollback.md`(直近 Submit のみの自動巻き戻し。本チケットは汎用 fork)
- session_store の既存 `fork` / `fork_at` を流用
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:09Z -->
## Migrated
Migrated from tickets/pod-session-fork.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,166 @@
---
id: 20260527-000010-prompt-eval-metrics
slug: prompt-eval-metrics
title: Prompt / Workflow 評価メトリクスと改善 Offer
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:10Z
updated_at: 2026-05-27T00:00:10Z
assignee: null
legacy_ticket: tickets/prompt-eval-metrics.md
---
## Migration reference
- legacy_ticket: tickets/prompt-eval-metrics.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# Prompt / Workflow 評価メトリクスと改善 Offer
## 背景
empirical prompt tuning pattern は、agent-facing な指示(Skill / slash command / prompt 等)を新規 subagent に実行させ、実行者の自己申告と指示側メトリクスを突き合わせて反復改善する手法である。insomnia では Workflow / Skill ingest / Knowledge / memory consolidation / usage metrics / Pod orchestration があるため、この手法を単なる「手順」ではなく、**agent-facing instruction の品質観測 pipeline** として扱える。
特に insomnia では以下をシステム側で観測できる。
- evaluator Pod の session id / history
- tool call / tool result
- usage tokens
- workflow / knowledge の明示使用ログ(use 回数、last used、source breakdown。`tickets/memory-usage-metrics.md`
- `model_invokation` 常駐注入の exposure cost 指標
- extract / consolidation による recurring pattern 抽出
- Workflow 自動書き込み禁止に基づく improvement offer
したがって、`/empirical-prompt-tuning` 相当の Workflow は、評価実行を orchestration するだけでなく、評価結果を構造化 event として残し、将来的に memory consolidation / usage metrics / Workflow improvement offer / `model_invokation` 判断へ接続するべきである。
## 要件
### `/empirical-prompt-tuning` Workflow
`.insomnia/workflow/empirical-prompt-tuning.md` を追加し、Workflow / Skill / prompt / Knowledge を評価対象として扱える手順を用意する。
Workflow は少なくとも以下を明示する。
- 評価対象 target の固定
- kind: workflow / skill / prompt / knowledge
- slug または path
- git revision または content hash
- Iteration 0: description / body consistency check
- scenario set の作成
- median 1 件
- edge 1〜2 件
- requirements checklist 3〜7 項目
- `[critical]` 項目を最低 1 つ含める
- evaluator Pod は毎回新規に spawn し、同じ evaluator を再利用しない
- evaluator Pod は実装者ではなく評価者として動く
- evaluator report は以下の構造にする
- Deliverable
- Requirement achievement
- Trace: Understanding / Planning / Execution / Formatting
- Unclear points: Issue / Cause / General Fix Rule
- Discretionary fill-ins
- Retries
- 1 iteration 1 theme の最小修正を原則とする
- Workflow / prompt の実ファイル書き換えは人間承認後に限る
- Workflow 自動生成 / 自動更新は禁止し、必要な改善は offer として人間に戻す
### 評価 event schema
評価結果を、将来の system metrics / memory consolidation に流せる構造化 event として定義する。
最低限の field:
```text
eval_run_id
target_kind
target_slug_or_path
target_revision_or_hash
scenario_id
scenario_kind: median | edge | holdout
evaluator_pod_name
evaluator_session_id
started_at / ended_at
success: bool
accuracy: number
critical_passed: bool
tool_call_count
tool_call_count_by_tool
input_tokens
output_tokens
cache_read_tokens / cache_write_tokens if available
scope_error_count
file_search_count
escalation_count
unclear_points[]
phase: Understanding | Planning | Execution | Formatting
issue
cause
general_fix_rule
discretionary_fill_ins[]
retries
```
初期実装で全 field が機械取得できない場合は、取得可能なものと evaluator self-report 由来のものを分ける。未取得 field は空にしてよいが、schema 上は将来埋められる形にする。
### Metrics / memory consolidation との接続
本チケットでは、評価 event を memory / metrics pipeline に接続する設計を明文化し、可能な最小実装を入れる。
接続方針:
- evaluator self-report は consolidation extract の活動抽出対象になる
- repeated `General Fix Rule` は consolidation が recurring failure pattern として統合できる
- recurring pattern は即 Knowledge 化せず、明示使用ログと Doctor / prompt-eval の事後評価を通す
- Workflow 改善は `.insomnia/workflow/*.md` へ自動書き込みせず、Notification / report / ticket などの offer に留める
- `model_invokation` ON 判断では、明示使用ログと resident exposure cost に加えて、eval success rate / unclear point count / description-body consistency を判断材料にする
### 評価指標の解釈
Claude Code 版の `tool_uses` を、insomnia では tool 種別ごとの偏りとして解釈する。
例:
- Glob / Grep が突出: references / 探索方針が prompt 内で弱い
- Read が突出: required context の入口が弱い
- scope error が出る: permission / worktree / escalation 境界が弱い
- SpawnPod / SendToPod が多い: orchestration の粒度や子 Pod 指示が曖昧
- ticket / git write に向かう: escalation criteria が弱い
定量指標は補助であり、Unclear points / Discretionary fill-ins / General Fix Rule を主指標とする。
### Failure pattern ledger
手書き台帳だけにせず、eval event から抽出可能な failure pattern として扱う。
- `General Fix Rule` を class-level pattern として正規化する
- 同じ pattern が複数 scenario / 複数 iteration / 複数 target で再発した場合、consolidation が decision / knowledge candidate / workflow improvement offer に統合できる
- 同じ pattern が 3 回以上再発した場合、局所 patch ではなく target prompt の構造変更を提案する
## 範囲外
- Workflow の自動書き換え
- Knowledge の即時自動作成
- `model_invokation` ON/OFF の完全自動切替
- evaluator Pod の永続ジョブキュー化
- prompt DSL 化
- LLM judge による主観的 A/B 比較の採用
- すべての metrics field の初期実装での完全自動取得
## 完了条件
- `.insomnia/workflow/empirical-prompt-tuning.md` が追加され、insomnia の evaluator Pod / metrics / memory consolidation 前提で記述されている
- Workflow は Iteration 0、scenario checklist、Trace、Issue / Cause / General Fix Rule、1 iteration 1 theme、人間承認 gate を明示している
- 評価 event schema が docs または ticket 内で定義されている
- eval event を memory consolidation / usage metrics / Workflow improvement offer / `model_invokation` 判断へ接続する方針が文書化されている
- 既存の Workflow 自動生成禁止・history に commit されない context input 禁止・memory consolidation 方針に反していない
- `ticket-intake-workflow` / `ticket-orchestrator-routing` / `worktree-workflow` のいずれか 1 件を対象に、構造審査または小規模 evaluator Pod 試走を行い、結果を記録している
## 参照
- empirical prompt tuning skill example(外部参照。取り込み時は必要最小限に一般化する)
- `docs/plan/workflow.md`
- `docs/plan/memory.md`
- `tickets/memory-usage-metrics.md`
- `ticket-intake-workflow.md` / `ticket-orchestrator-routing.md`
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:10Z -->
## Migrated
Migrated from tickets/prompt-eval-metrics.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,81 @@
---
id: 20260527-000015-tui-navigation-mode-design
slug: tui-navigation-mode-design
title: TUI: navigation mode / block focus の設計
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:15Z
updated_at: 2026-05-27T00:00:15Z
assignee: null
legacy_ticket: tickets/tui-navigation-mode-design.md
---
## Migration reference
- legacy_ticket: tickets/tui-navigation-mode-design.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# TUI: navigation mode / block focus の設計
## 背景
TUI の操作は現在 composer を中心にしており、履歴 block / task 表示 / queued input / system 操作の間を移動する統一的な navigation model はまだない。今後 command mode、manual compact、rollback、Pod picker、queue 編集などが増えると、Ctrl/Alt shortcut だけでは操作体系が散らばる。
一方で、通常入力は最優先で守る必要がある。特に streaming 中の入力取りこぼしや rollback restore、Run 中 input queue を入れたことで、composer の文字入力を暗黙操作で壊さないことが重要になっている。
本チケットは navigation mode のアイデアを保持する設計 ticket であり、すぐ実装する前提ではない。
## アイデア
- 通常は composer mode。
- 文字入力、Enter submit/queue、`@` / `#` / `/` 補完を優先する。
- `Esc` など明示操作で navigation mode に入る。
- 履歴 block / task pane / queued input / picker 的 UI に focus を移す。
- focus があることを視覚的に分かるようにする。
- navigation mode では `j/k` または `↑/↓` で block focus / scroll を行う。
- `i` / `Enter` / `Esc` で composer に戻る案。
- composer のカーソルが最上行にある時の `↑` で履歴へ抜ける自然操作も候補。
- ただし multi-line input / IME / completion / typed segment と衝突しやすいため、初期実装では慎重に扱う。
- 暗黙 focus 移動より、明示 navigation mode を優先する案が安全。
- command mode (`:`) とは分ける。
- command mode は system command 入力。
- navigation mode は画面上の対象選択 / scroll / block action。
## 検討事項
- mode 名と status/actionbar 表示。
- composer mode から navigation mode へ入る key。
- navigation mode から composer mode へ戻る key。
- `↑/↓` を composer cursor movement と block focus movement のどちらに使うか。
- block focus の単位。
- Turn header
- User message
- Assistant block
- Tool call/result
- System message
- Task row
- Queued input row
- focused block に対する action。
- copy
- expand/collapse
- retry/fork/rollback など将来操作
- scrollback と block focus の関係。
- search (`/` ではなく別 key が必要。`/` は WorkflowRef と衝突する可能性)。
- mouse support を入れるか。
## 完了条件(未確定)
- navigation mode の keymap と UI 表示方針が決まる。
- composer 入力を壊さない focus 移動ルールが決まる。
- block focus の最小単位が決まる。
- command mode / queue / rollback / Pod picker と衝突しない。
- 実装 ticket に分割できる。
## 範囲外
- 今すぐの実装。
- command mode の実装(`tickets/tui-command-mode.md`)。
- compact command の実装。
- Vim 完全互換。
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:15Z -->
## Migrated
Migrated from tickets/tui-navigation-mode-design.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,104 @@
---
id: 20260527-000018-tui-user-model-setup
slug: tui-user-model-setup
title: TUI: ユーザーマニフェストのモデル設定 wizard
status: open
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:18Z
updated_at: 2026-05-27T00:00:18Z
assignee: null
legacy_ticket: tickets/tui-user-model-setup.md
---
## Migration reference
- legacy_ticket: tickets/tui-user-model-setup.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# TUI: ユーザーマニフェストのモデル設定 wizard
## 背景
spawn UI`tickets/tui-pod-spawn-ui.md`)が `[model]` を user / project の cascade レイヤから取る前提なので、初回起動のユーザーは事前に `~/.config/insomnia/manifest.toml` を手で書く必要がある。catalog(`crates/provider/src/catalog.rs`)に provider / model の一覧と `AuthHint`API key の env 名や Codex OAuth 等の認証方式)が既に揃っているので、これを使って TUI 内で対話的にセットアップできるようにする。
`provider::catalog` の公開 API:
- `load_providers() -> Vec<ProviderEntry>`: builtin + user override マージ済み
- `load_models() -> Vec<ModelEntry>`: 同上
- `ProviderEntry`: `id` / `display_name` / `scheme` / `auth_hint`
- `ModelEntry`: `id` / `provider` / `capability`
- `AuthHint`: `None` / `ApiKey { env: Option<String> }` / `CodexOAuth`
これらが「UI で何を選ばせ、何を聞くか」を直接ガイドしてくれる構造になっている。
## 要件
### 起動経路
- 専用サブコマンド `tui setup-model`(仮)として alt-screen TUI で起動する
- 引数なしで叩くと既存の spawn flow に入るので、それとは別の入口
### Wizard フロー
1. **provider 選択**: `load_providers()` の結果をリスト表示。`display_name` を見せ、上下キー + Enter で選択
2. **model 選択**: 選んだ provider の `id``load_models()` をフィルタしてリスト表示。1 つだけならスキップ可
3. **認証情報入力**: 選んだ provider の `auth_hint` で分岐
- `None` → スキップ
- `ApiKey { env: Some(name) }` → 「環境変数 `<name>` を使う」または「key ファイルパスを入力」を選ばせる
- `ApiKey { env: None }` → key ファイルパス入力(絶対パス推奨、ホーム展開はする)
- `CodexOAuth` → 「`codex login` で OAuth を済ませてください」案内 + `~/.codex/auth.json` の存在チェック
4. **確認画面**: 書き込み内容のプレビュー(生成される TOML)を表示、Enter で確定 / Esc でキャンセル
5. **書き込み**: `~/.config/insomnia/manifest.toml`(または `$XDG_CONFIG_HOME` 配下、`manifest::user_manifest_path()`)に `[model]` を書く
### 書き込みフォーマット
catalog 由来なので `ref` 形式を採用する:
```toml
[model]
ref = "<provider_id>/<model_id>"
[model.auth]
kind = "api_key"
file = "/abs/path/to/key"
```
`AuthHint::None` の場合は `[model.auth]` を省く。`CodexOAuth` の場合は `kind = "codex_oauth"`
### 既存ファイルの扱い
`~/.config/insomnia/manifest.toml` が既に存在する場合:
- `[model]` セクションが無い → 末尾に追加
- `[model]` セクションが既にある → 上書き確認を出す(既存の値をプレビュー表示してから)
- ファイル全体が壊れた TOML → エラー表示してキャンセル
### キャンセル / エラー経路
- どのステップでも Esc / Ctrl-C で抜けられる。書き込み前ならファイルは触らない
- catalog 読み込み失敗 / ファイル書き込み失敗は alt-screen 内でエラー表示してから終了
## 設計で決めること
- **API key ファイルパスの入力 UX**: テキスト入力欄でフリーフォーム、補完なしで良いか、`~` / `$HOME` 展開するか
- **環境変数で済ませる選択肢の見せ方**: ApiKey で env 指定がある場合、デフォルト「env を使う」かデフォルト「key ファイルを使う」か
- **多 provider / 多 model 時の選択 UI**: シンプルな縦リストか、検索フィルタ付きか
- **既存 `[model]` の上書き確認の粒度**: TOML 全体 diff か、変わるキーだけハイライトか
## 完了条件
- `tui setup-model` サブコマンドで wizard が起動する
- catalog から provider / model 一覧を取って表示・選択できる
- `AuthHint` の各バリアントに対応した入力 UI が動く
- 確定すると `~/.config/insomnia/manifest.toml``[model]` が書き込まれる
- 既存ファイルの `[model]` 上書き時は確認が出る
- セットアップ後に spawn flow(引数なし `tui` 起動)が model resolve エラー無しで Pod を spawn できる
## 範囲外
- catalog 自体の編集(新規 provider / model の追加)UI。`providers.toml` / `models.toml` の手書き運用は維持
- 複数モデル設定(`[compaction.model]` 等)の wizard 化
- project manifest (`.insomnia/manifest.toml`) への書き込み。本チケットは user 層のみ
- spawn flow からの自動誘導(model 不在検出時に「`m` で setup wizard を起動」分岐)。本チケット完了後に spawn UI 側で別途検討
@@ -0,0 +1,7 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:18Z -->
## Migrated
Migrated from tickets/tui-user-model-setup.md. No legacy review file was present at migration time.
---
@@ -0,0 +1,412 @@
# Crate boundary audit
Date: 2026-05-28
## summary
The workspace dependency graph is broadly acyclic and mostly layered in the expected direction: `protocol` / `lint-common` / proc-macros sit at the bottom, `llm-worker` / `manifest` / `tools` / `provider` / `session-store` provide shared infrastructure, and `pod` / `tui` are orchestration or UI layers. I did not find a hard Cargo-level cycle or an obvious UI crate being depended on by a lower crate.
The main boundary problems are subtler:
1. `protocol` exposes several public wire payloads as `serde_json::Value` while documenting them as the JSON form of `session_store::*` types. This avoids a Rust dependency edge but creates a hidden schema dependency from `protocol`/clients to `session-store`.
2. `workflow` depends on `memory` for `WorkspaceLayout`, and `memory::WorkspaceLayout` owns workflow paths. This makes `memory` a cross-domain workspace-layout hub rather than only the memory subsystem.
3. Several lower/shared crates have comments/doc-comments explaining `Pod`, `TUI`, controller, prompt-catalog, or downstream orchestration behavior. Most are acceptable integration-contract notes, but a few are implementation knowledge that should move upward or be generalized.
No code, formatting, commits, merges, or project-record files outside `artifacts/` were changed.
## inspected commands / files
### Commands run
- `cargo metadata --no-deps --format-version 1 | jq ... > artifacts/deps.txt`
- Extracted workspace-internal normal/dev dependency edges.
- `cargo metadata --no-deps --format-version 1 | jq ... > artifacts/reverse-deps.txt`
- Extracted reverse dependency summary.
- `rg -n 'pub (struct|enum|fn|mod|trait|type|use) ...' crates --glob '*.rs' > artifacts/public-concept-hits.txt`
- Searched public APIs for boundary-relevant terms (`Pod`, `TUI`, `Workflow`, `Manifest`, `Memory`, `Session`, etc.).
- `rg -n '(^\s*(//!|///|//)\s?.*(...))' crates --glob '*.rs' > artifacts/comment-concept-hits.txt`
- Searched comments/doc-comments for crate names and upper-layer concepts.
- `rg -n 'TUI / GUI|session_store::|parent Controller|Pod treats|Pod side|...' ... > artifacts/suspicious-excerpts.txt`
- Narrowed suspicious comment excerpts.
- `rg -n 'use (session_store|pod_registry|llm_worker|manifest)::|...' crates/tui/src`
- Checked why TUI depends on lower internal crates.
- `rg -n 'WorkspaceLayout|memory::' crates/workflow/src`
- Checked `workflow -> memory` dependency use.
Failed exploratory commands:
- `python` / `python3` parse attempts failed because Python was not available in the environment; switched to `cargo metadata` + `jq`.
Supplemental raw outputs left in the artifact directory:
- `deps.txt`, `reverse-deps.txt`
- `deps-numbered.txt`, `reverse-deps-numbered.txt`
- `public-concept-hits.txt`
- `comment-concept-hits.txt`
- `suspicious-excerpts.txt`
### Main files inspected directly
- Root/workspace:
- `Cargo.toml`
- `work-items/open/20260528-131317-crate-boundary-audit/item.md`
- Cargo manifests:
- `crates/protocol/Cargo.toml`
- `crates/manifest/Cargo.toml`
- `crates/llm-worker/Cargo.toml`
- `crates/pod/Cargo.toml`
- `crates/client/Cargo.toml`
- `crates/tui/Cargo.toml`
- `crates/memory/Cargo.toml`
- `crates/workflow/Cargo.toml`
- `crates/provider/Cargo.toml`
- `crates/session-store/Cargo.toml`
- `crates/pod-registry/Cargo.toml`
- `crates/session-metrics/Cargo.toml`
- `crates/tools/Cargo.toml`
- `crates/daemon/Cargo.toml`
- `crates/lint-common/Cargo.toml`
- `crates/llm-worker-macros/Cargo.toml`
- Public/API and suspicious source files:
- `crates/protocol/src/lib.rs`
- `crates/manifest/src/lib.rs`
- `crates/manifest/src/model.rs`
- `crates/llm-worker/src/lib.rs`
- `crates/llm-worker/src/interceptor.rs`
- `crates/llm-worker/src/llm_client/types.rs`
- `crates/pod/src/lib.rs`
- `crates/pod/src/pod.rs` (grep/read excerpts)
- `crates/pod/src/spawn/comm_tools.rs` (grep excerpts)
- `crates/client/src/lib.rs`
- `crates/client/src/spawn.rs`
- `crates/tui/src/app.rs` (grep excerpts)
- `crates/tui/src/spawn.rs` (grep excerpts)
- `crates/tui/src/picker.rs` (grep excerpts)
- `crates/memory/src/lib.rs`
- `crates/memory/src/scope.rs`
- `crates/memory/src/workspace.rs`
- `crates/memory/src/extract/mod.rs` (grep excerpts)
- `crates/memory/src/consolidate/mod.rs` (grep excerpts)
- `crates/memory/src/resident.rs` (grep excerpts)
- `crates/workflow/src/lib.rs`
- `crates/workflow/src/linter.rs` (grep excerpts)
- `crates/workflow/src/scope.rs` (grep excerpts)
- `crates/workflow/src/workflow.rs` (grep excerpts)
- `crates/session-store/src/lib.rs`
- `crates/session-store/src/segment.rs`
- `crates/session-store/src/segment_log.rs` (grep excerpts)
- `crates/session-store/src/system_item.rs`
- `crates/session-store/src/pod_metadata.rs`
- `crates/pod-registry/src/lib.rs`
- `crates/provider/src/lib.rs`
- `crates/tools/src/lib.rs`
## dependency graph overview
Internal dependency edges from `cargo metadata --no-deps`:
```text
client -> manifest, protocol
daemon -> manifest, protocol
lint-common -> (none)
llm-worker -> llm-worker-macros
llm-worker-macros -> (none)
manifest -> llm-worker, protocol
memory -> lint-common, llm-worker, manifest
pod -> llm-worker, manifest, memory, pod-registry, protocol, provider, session-metrics, session-store, tools, workflow
pod-registry -> manifest, session-store
protocol -> (none)
provider -> llm-worker, manifest
session-metrics -> session-store
session-store -> llm-worker, protocol
tools -> llm-worker, manifest
tui -> client, llm-worker, manifest, pod-registry, protocol, session-store; dev-dep tools
workflow -> lint-common, manifest, memory
```
Reverse summary:
```text
client <- tui
lint-common <- memory, workflow
llm-worker <- manifest, memory, pod, provider, session-store, tools, tui
manifest <- client, daemon, memory, pod, pod-registry, provider, tools, tui, workflow
memory <- pod, workflow
pod-registry <- pod, tui
protocol <- client, daemon, manifest, pod, session-store, tui
provider <- pod
session-metrics <- pod
session-store <- pod, pod-registry, session-metrics, tui
tools <- pod, tui
workflow <- pod
```
This is directionally reasonable for orchestration-heavy code: `pod` is the main integrator; `tui` sits above `client` but also reads lower schemas; `protocol` has no Rust workspace dependencies.
## dependency/interface findings grouped by severity
### Severity: actual problem / should ticket
#### 1. `protocol` public API has hidden `session-store` schema coupling through `serde_json::Value`
Evidence:
- `crates/protocol/src/lib.rs:237` documents `Event::SystemItem.item` as the JSON form of `session_store::SystemItem`.
- `crates/protocol/src/lib.rs:394` documents `Event::Snapshot.entries` as the JSON form of `session_store::LogEntry`.
- `crates/protocol/src/lib.rs:419` documents `Event::SegmentRotated.entry` as the JSON form of `session_store::LogEntry::SegmentStart`.
- `crates/tui/src/app.rs:1236` and nearby lines deserialize snapshot entries back into `session_store::LogEntry`.
- `crates/tui/src/app.rs:1277` and nearby lines deserialize `Event::SystemItem.item` into `session_store::SystemItem`.
Why this is a boundary issue:
- `protocol` is dependency-free at the Cargo level, but its wire contract is not actually self-owned: clients must know `session-store` schemas to reconstruct state correctly.
- The type system cannot enforce compatibility between `protocol` and `session-store` because the public protocol type is only `serde_json::Value`.
- This explains why `tui` depends directly on `session-store` despite also depending on `client`/`protocol`.
Recommended direction:
- Extract the wire-stable log/system-item DTOs into a neutral crate, or move protocol-facing DTOs into `protocol` and have `session-store` convert to/from them.
- Avoid public protocol docs that say “this is `session_store::X` JSON” unless `session-store` is intentionally part of the protocol contract and typed as such.
#### 2. `workflow -> memory` dependency exists for shared workspace layout
Evidence:
- `crates/workflow/Cargo.toml:11` depends on `memory`.
- `crates/workflow/src/linter.rs:5`, `crates/workflow/src/scope.rs:6`, `crates/workflow/src/workflow.rs:17` use `memory::WorkspaceLayout`.
- `crates/memory/src/workspace.rs:8` includes `<root>/.insomnia/workflow/<slug>.md` in memory's layout documentation.
- `crates/memory/src/workspace.rs:16-18` says workflows are human-managed and live one level up under `.insomnia/workflow/`.
- `crates/memory/src/workspace.rs:127-165` exposes `workflow_dir()` / `workflow_path()` from the memory crate.
Why this is a boundary issue:
- `workflow` is conceptually a sibling subsystem, not a consumer of generated memory state.
- The current dependency is only for path layout. That makes `memory` own cross-subsystem workspace conventions and forces workflow to import a memory-domain crate for non-memory concerns.
- This is not severe yet, but it will make future workflow growth pull against crate ownership.
Recommended direction:
- Extract `WorkspaceLayout` / `.insomnia` path conventions into a neutral crate or a neutral module under `manifest`/new `workspace-layout` crate.
- Then make `memory` and `workflow` both depend on that neutral layout instead of depending on each other.
### Severity: suspicious but currently acceptable
#### 3. `session-store` owns Pod metadata and spawned-child metadata
Evidence:
- `crates/session-store/src/pod_metadata.rs:1-6` defines “Pod metadata persistence API”.
- `crates/session-store/src/pod_metadata.rs:42-60` defines `PodSpawnedScopeRule` / `PodSpawnedChild`, including delegated scope and `callback_address`.
- `crates/session-store/src/pod_metadata.rs:62-88` exposes `PodMetadata` and `PodMetadataStore` publicly.
Assessment:
- This is Pod/orchestration-specific state inside a crate named `session-store`.
- It is acceptable if `session-store` is intentionally “insomnia persistence primitives”, not a generic conversation-log crate. Current project decisions appear to lean that way.
- If the intended boundary is “session-store only stores sessions/segments/logs”, this should be split or renamed. If the intended boundary is “session-store stores all durable Pod state”, the naming/docs should say that explicitly.
Recommended direction:
- No immediate refactor unless the ownership goal changes.
- Clarify crate-level docs: either broaden `session-store`'s stated responsibility to durable Pod/session persistence, or split Pod metadata into a `pod-state`/`pod-metadata` crate.
#### 4. TUI directly depends on persistence/registry crates
Evidence:
- `crates/tui/Cargo.toml` depends on `session-store`, `pod-registry`, `manifest`, `llm-worker`, and `protocol` in addition to `client`.
- `crates/tui/src/picker.rs` uses `pod_registry::{LockFileGuard, default_registry_path}` and `session_store::{...}`.
- `crates/tui/src/app.rs:1236-1298` parses `session_store::LogEntry` / `session_store::SystemItem`.
- `crates/tui/src/spawn.rs:408-409` uses `session_store::FsStore` and `restore_by_segment` for resume-related paths.
Assessment:
- TUI is a top-level crate, so dependency direction is allowed.
- The direct `session-store` parse dependency is largely a symptom of finding #1: protocol sends untyped JSON whose real schema lives in `session-store`.
- Direct `pod-registry` access for picker/runtime discovery may be acceptable for a local-first TUI, but it bypasses a cleaner “TUI talks protocol/client only” boundary.
Recommended direction:
- Fix protocol DTO ownership first.
- After that, re-evaluate whether TUI still needs direct `session-store` and `pod-registry` dependencies or whether picker/discovery can move behind `client`/`protocol` APIs.
#### 5. `manifest -> llm-worker` dependency is acceptable but should remain one-way
Evidence:
- `crates/manifest/Cargo.toml` depends on `llm-worker` and `protocol`.
- `crates/manifest/src/model.rs:17-19` re-exports `llm_worker::llm_client::capability::{ModelCapability, ReasoningControl, ReasoningEffort}`.
Assessment:
- This is a reasonable tradeoff to avoid duplicate model-capability types.
- It does mean `manifest` is not a pure data crate independent of worker runtime types.
- The boundary remains acceptable as long as `llm-worker` does not depend back on `manifest`, and provider-level resolution stays in `provider`.
### Severity: no issue found
- No Rust workspace dependency cycle was found in the inspected graph.
- I did not find lower crates depending on `tui` or `client` implementation crates.
- `client -> protocol/manifest` and `pod -> provider/tools/session-store/memory/workflow` are directionally appropriate.
- `provider -> llm-worker/manifest` is appropriate: provider constructs concrete `LlmClient` implementations from resolved model configuration.
- `tools -> llm-worker/manifest` is appropriate: tools expose `ToolDefinition`s and enforce manifest scopes.
- `pod-registry -> session-store` is acceptable if registry entries need session/segment identity and durable state coordination.
## comment/doc-comment findings
### Problematic or should be generalized
#### `protocol` describes parent controller and pod-registry side effects
- `crates/protocol/src/lib.rs:65-70`
- `PodEvent` docs say the “parent Controller applies variant-specific side effects (registry / pod-registry updates)”.
- This is implementation knowledge from the `pod` crate inside a dependency-free protocol crate.
- Better: state the wire contract (“event is delivered to the parent; receiver is responsible for handling lifecycle effects”) and keep registry-specific behavior in `pod` docs.
#### `protocol` documents `session_store::*` JSON shapes as protocol payloads
- `crates/protocol/src/lib.rs:237`
- `crates/protocol/src/lib.rs:394`
- `crates/protocol/src/lib.rs:419`
This is the comment-level manifestation of the public-interface issue in finding #1.
#### `llm-worker` public request docs mention Pod-specific cache-key choice
- `crates/llm-worker/src/llm_client/types.rs:523-526`
- `Request::cache_key` doc says pod side is expected to pass `SegmentId`.
- `llm-worker` should expose the generic concept: a stable caller-provided conversation/cache namespace key.
- Pod's choice of `SegmentId` belongs in `pod` docs/tests, not in the generic request type.
#### `memory` docs prescribe Pod assembly details
- `crates/memory/src/lib.rs:3-7`
- Says generic CRUD tools must not touch memory/knowledge and Pod is responsible for denying them.
- `crates/memory/src/scope.rs:4-8`
- Says Pod is expected to call `deny_write_rules` and pass the result to `tools::ScopedFs`.
- `crates/memory/src/extract/mod.rs:3-14`
- Explains Pod post-run hook, `PromptCatalog`, `PodPrompt::MemoryExtractSystem`, and pointer persistence responsibility.
- `crates/memory/src/consolidate/mod.rs:5-15`
- Explains Pod assembling a disposable Worker and using `PodPrompt::MemoryConsolidationSystem`.
- `crates/memory/src/resident.rs:3-11`
- Says surfaces are used by the Pod system-prompt assembler and Pod IPC layer for TUI `#` completion.
Assessment:
- These are understandable because `memory` is currently a helper subsystem consumed by `pod`.
- They nevertheless make `memory` read like it is documenting Pod orchestration rather than memory-owned contracts.
- Prefer caller-neutral wording: “the orchestrator/caller registers these tools”, “the caller persists the pointer”, “completion consumers may use ...”. Keep Pod-specific sequence docs in `pod`.
### Suspicious but acceptable integration-contract comments
#### `protocol::Segment` docs mention TUI/GUI and Pod parsing behavior
- `crates/protocol/src/lib.rs:116-126`
- Mentions richer clients (TUI/GUI) producing typed atoms and Pod not re-parsing flattened strings.
- `crates/protocol/src/lib.rs:143-153`
- Mentions Pod resolving `FileRef` and treating unknown variants as unresolved input.
- `crates/protocol/src/lib.rs:222-231`
- Mentions additional TUI/GUI instances rendering user messages.
Assessment:
- Mentioning client classes can be acceptable in protocol docs when it explains wire semantics.
- The Pod behavior details are more debatable; they should be limited to required protocol semantics, not specific controller implementation.
#### `session-store::SystemItem` mentions TUI typed rendering
- `crates/session-store/src/system_item.rs:27-35`
- `crates/session-store/src/system_item.rs:49-52`
Assessment:
- It is valid to document why typed payload exists.
- “so the TUI can render” should probably be generalized to “so clients can render” because `session-store` is lower than `tui`.
#### `session-store::segment` mentions Pod as typical caller
- `crates/session-store/src/segment.rs:3-5`
- `crates/session-store/src/segment.rs:38-40`
- `crates/session-store/src/segment.rs:175-180`
- `crates/session-store/src/segment.rs:252-254`
Assessment:
- Mostly acceptable because Pod is currently the primary writer.
- Better wording would say “caller/orchestrator” first and optionally “e.g. Pod” only where it clarifies current integration.
#### `client` docs mention TUI/GUI/E2E
- `crates/client/src/lib.rs:9`
- `crates/client/src/spawn.rs:4-10`
- `crates/client/src/spawn.rs:92-96`
Assessment:
- Acceptable: `client` is explicitly a library for UI/GUI/E2E callers to speak Pod protocol. These are consumer examples rather than lower-layer implementation leakage.
#### `llm-worker::Interceptor` docs mention Pod as an upper layer
- `crates/llm-worker/src/interceptor.rs:3-6`
- `crates/llm-worker/src/interceptor.rs:122-126`
- `crates/llm-worker/src/interceptor.rs:140-146`
Assessment:
- Mostly acceptable: the docs explicitly say Worker does not know higher-level concepts and Pod is only an example upper layer.
- For stricter boundary hygiene, prefer “upper layers/orchestrators” and avoid naming Pod except in examples.
## acceptable dependency-aware comments criteria
I treated a comment as acceptable when it met at least one of these criteria:
1. It explains a public wire or file-format contract that consumers must honor, without prescribing one consumer's private implementation.
2. It names a higher layer only as an example (`e.g. Pod`) while the API remains generic and caller-owned.
3. It documents an intentional direction-of-control boundary, such as “the lower crate exposes a hook; upper layers implement policy”.
4. It references another crate that the current crate actually depends on and whose type or function is part of the local API.
5. It appears in tests/examples whose purpose is cross-crate contract verification.
I treated a comment as problematic when it did any of the following:
1. A lower crate explains what a dependent higher crate currently does internally.
2. A lower crate's public docs define a payload as another higher crate's private or semi-private JSON schema.
3. A shared subsystem describes its API mainly as a sequence of Pod/TUI orchestration steps, rather than a caller-neutral contract.
4. The comment reveals a hidden dependency that Cargo cannot type-check.
## recommended follow-up tickets
1. **Typed protocol snapshot/system-item payloads**
- Goal: remove `protocol` public `serde_json::Value` payloads whose real schemas are `session_store::*`.
- Candidate implementation directions:
- Move wire DTOs for log entries/system items into `protocol`, with `session-store` converting to/from them; or
- Extract a neutral `session-log-schema` / `wire-log` crate used by both `protocol` and `session-store`.
- Success condition: TUI/client code can parse snapshots/system items using protocol-owned typed structures, not `session_store::LogEntry` hidden behind JSON.
2. **Extract neutral workspace layout from `memory`**
- Goal: remove `workflow -> memory` when the only need is `.insomnia` path layout.
- Candidate implementation directions:
- New neutral crate/module for `WorkspaceLayout`; or
- Move `.insomnia` path layout into `manifest` if that crate is intended to own workspace configuration.
- Success condition: `workflow` and `memory` are siblings depending on a neutral layout owner.
3. **Boundary-comment hygiene pass**
- Goal: replace reverse-knowledge comments in lower/shared crates with caller-neutral wording.
- Scope:
- `protocol/src/lib.rs` controller/session-store JSON wording.
- `llm-worker/src/llm_client/types.rs` Pod `SegmentId` cache-key wording.
- `memory/src/{scope,extract,consolidate,resident}.rs` Pod/TUI orchestration wording.
- `session-store/src/{system_item,segment}.rs` TUI/Pod-specific wording where not required.
- Success condition: comments explain local contracts and extension points; dependent-crate implementation details live in the dependent crate.
4. **Clarify `session-store` crate responsibility**
- Goal: decide whether `session-store` is only session/segment log storage or the broader durable Pod-state persistence crate.
- If broader: update crate docs/naming comments to say so.
- If narrower: split `pod_metadata` into a Pod-owned persistence crate/module.
## unresolved questions
1. Is `protocol` intended to be the sole owner of all stable wire DTOs, or is `session-store` intentionally part of the protocol contract despite the current `serde_json::Value` indirection?
2. Is `session-store` deliberately the durable state crate for all Pod metadata, or should it be constrained to conversation/session logs?
3. Should `WorkspaceLayout` be considered a memory-domain concept, or a repository/workspace-domain concept shared by memory, knowledge, and workflow?
4. Should TUI remain allowed to inspect local registry/session files directly for picker and restore UX, or should those capabilities move behind `client`/`protocol` APIs?
5. Are comments allowed to name the primary current consumer (`Pod`) when documenting a generic lower-layer extension point, or should comments avoid such names unless the type itself is Pod-specific?
@@ -0,0 +1,58 @@
1 client:
2 -> manifest [normal]
3 -> protocol [normal]
4 daemon:
5 -> manifest [normal]
6 -> protocol [normal]
7 lint-common:
8 (no workspace deps)
9 llm-worker:
10 -> llm-worker-macros [normal]
11 llm-worker-macros:
12 (no workspace deps)
13 manifest:
14 -> llm-worker [normal]
15 -> protocol [normal]
16 memory:
17 -> lint-common [normal]
18 -> llm-worker [normal]
19 -> manifest [normal]
20 pod:
21 -> llm-worker [normal]
22 -> manifest [normal]
23 -> memory [normal]
24 -> pod-registry [normal]
25 -> protocol [normal]
26 -> provider [normal]
27 -> session-metrics [normal]
28 -> session-store [normal]
29 -> tools [normal]
30 -> workflow [normal]
31 pod-registry:
32 -> manifest [normal]
33 -> session-store [normal]
34 protocol:
35 (no workspace deps)
36 provider:
37 -> llm-worker [normal]
38 -> manifest [normal]
39 session-metrics:
40 -> session-store [normal]
41 session-store:
42 -> llm-worker [normal]
43 -> protocol [normal]
44 tools:
45 -> llm-worker [normal]
46 -> manifest [normal]
47 tui:
48 -> client [normal]
49 -> llm-worker [normal]
50 -> manifest [normal]
51 -> pod-registry [normal]
52 -> protocol [normal]
53 -> session-store [normal]
54 -> tools [dev]
55 workflow:
56 -> lint-common [normal]
57 -> manifest [normal]
58 -> memory [normal]
@@ -0,0 +1,58 @@
client:
-> manifest [normal]
-> protocol [normal]
daemon:
-> manifest [normal]
-> protocol [normal]
lint-common:
(no workspace deps)
llm-worker:
-> llm-worker-macros [normal]
llm-worker-macros:
(no workspace deps)
manifest:
-> llm-worker [normal]
-> protocol [normal]
memory:
-> lint-common [normal]
-> llm-worker [normal]
-> manifest [normal]
pod:
-> llm-worker [normal]
-> manifest [normal]
-> memory [normal]
-> pod-registry [normal]
-> protocol [normal]
-> provider [normal]
-> session-metrics [normal]
-> session-store [normal]
-> tools [normal]
-> workflow [normal]
pod-registry:
-> manifest [normal]
-> session-store [normal]
protocol:
(no workspace deps)
provider:
-> llm-worker [normal]
-> manifest [normal]
session-metrics:
-> session-store [normal]
session-store:
-> llm-worker [normal]
-> protocol [normal]
tools:
-> llm-worker [normal]
-> manifest [normal]
tui:
-> client [normal]
-> llm-worker [normal]
-> manifest [normal]
-> pod-registry [normal]
-> protocol [normal]
-> session-store [normal]
-> tools [dev]
workflow:
-> lint-common [normal]
-> manifest [normal]
-> memory [normal]
@@ -0,0 +1,202 @@
crates/llm-worker-macros/src/lib.rs:257: pub fn #definition_name(&self) -> ::llm_worker::tool::ToolDefinition {
crates/provider/src/codex_oauth/error.rs:49: pub fn to_client_error(self) -> ClientError {
crates/session-store/src/system_item.rs:114:pub fn render_pod_event(event: &PodEvent) -> String {
crates/memory/src/tool/read.rs:182:pub fn read_tool(layout: WorkspaceLayout) -> ToolDefinition {
crates/provider/src/codex_oauth/mod.rs:62: pub fn from_default_home() -> Result<Self, ClientError> {
crates/llm-worker/src/interceptor.rs:97:pub struct ToolCallInfo {
crates/llm-worker/src/interceptor.rs:107:pub struct ToolResultInfo {
crates/memory/src/tool/write.rs:188:pub fn write_tool(layout: WorkspaceLayout) -> ToolDefinition {
crates/session-store/src/lib.rs:69:pub type SessionId = uuid::Uuid;
crates/session-store/src/lib.rs:75:pub fn new_session_id() -> SessionId {
crates/memory/src/tool/query.rs:473:pub fn memory_query_tool(layout: WorkspaceLayout, config: QueryConfig) -> ToolDefinition {
crates/memory/src/tool/query.rs:488:pub fn knowledge_query_tool(layout: WorkspaceLayout, config: QueryConfig) -> ToolDefinition {
crates/session-store/src/pod_metadata.rs:18:pub struct PodActiveSegmentRef {
crates/session-store/src/pod_metadata.rs:26: pub fn pending_segment(session_id: SessionId) -> Self {
crates/session-store/src/pod_metadata.rs:34: pub fn active_segment(session_id: SessionId, segment_id: SegmentId) -> Self {
crates/session-store/src/pod_metadata.rs:46:pub struct PodSpawnedScopeRule {
crates/session-store/src/pod_metadata.rs:55:pub struct PodSpawnedChild {
crates/session-store/src/pod_metadata.rs:64:pub struct PodMetadata {
crates/session-store/src/pod_metadata.rs:74: pub fn new(pod_name: impl Into<String>, active: Option<PodActiveSegmentRef>) -> Self {
crates/session-store/src/pod_metadata.rs:88:pub trait PodMetadataStore: Send + Sync {
crates/memory/src/tool/mod.rs:33:pub enum MemoryToolKind {
crates/session-store/src/segment_log.rs:174:pub struct PodScopeSnapshot {
crates/llm-worker/src/worker.rs:57:pub enum ToolRegistryError {
crates/llm-worker/src/worker.rs:483: pub fn on_tool_result(&mut self, callback: impl Fn(&ToolResult) + Send + Sync + 'static) {
crates/llm-worker/src/worker.rs:528: pub fn tool_server_handle(&self) -> ToolServerHandle {
crates/llm-worker/src/worker.rs:1646: pub fn set_tool_output_limits(&mut self, limits: Option<ToolOutputLimits>) {
crates/memory/src/tool/edit.rs:268:pub fn edit_tool(layout: WorkspaceLayout) -> ToolDefinition {
crates/llm-worker/src/llm_client/transport.rs:98: pub fn with_http_client(mut self, client: reqwest::Client) -> Self {
crates/memory/src/tool/delete.rs:98:pub fn delete_tool(layout: WorkspaceLayout) -> ToolDefinition {
crates/llm-worker/src/lib.rs:56:pub use callback::{TextBlockScope, ThinkingBlockScope, ToolUseBlockScope};
crates/llm-worker/src/lib.rs:57:pub use handler::ToolUseBlockStart;
crates/llm-worker/src/lib.rs:60:pub use tool::{ToolCall, ToolOutputLimits, ToolResult};
crates/memory/src/scope.rs:19:pub fn deny_write_rules(layout: &WorkspaceLayout) -> Vec<ScopeRule> {
crates/llm-worker/src/llm_client/error.rs:7:pub enum ClientError {
crates/llm-worker/src/llm_client/error.rs:107:pub fn is_retryable(error: &ClientError) -> bool {
crates/llm-worker/src/tool.rs:16:pub enum ToolError {
crates/llm-worker/src/tool.rs:48:pub struct ToolOutputLimits {
crates/llm-worker/src/tool.rs:99:pub struct ToolOutput {
crates/llm-worker/src/tool.rs:135:pub struct ToolMeta {
crates/llm-worker/src/tool.rs:190:pub type ToolDefinition = Arc<dyn Fn() -> (ToolMeta, Arc<dyn Tool>) + Send + Sync>;
crates/llm-worker/src/tool.rs:245:pub trait Tool: Send + Sync {
crates/llm-worker/src/tool.rs:265:pub struct ToolCall {
crates/llm-worker/src/tool.rs:279:pub struct ToolResult {
crates/llm-worker/src/tool.rs:294: pub fn from_output(tool_use_id: impl Into<String>, output: ToolOutput) -> Self {
crates/memory/src/error.rs:10:pub enum MemoryError {
crates/pod-registry/src/error.rs:11:pub enum ScopeLockError {
crates/llm-worker/src/llm_client/capability.rs:41:pub enum ToolCallingSupport {
crates/llm-worker/src/tool_server.rs:13:pub enum ToolServerError {
crates/llm-worker/src/tool_server.rs:27:pub struct ToolServer {
crates/llm-worker/src/tool_server.rs:39: pub fn handle(&self) -> ToolServerHandle {
crates/llm-worker/src/tool_server.rs:49:pub struct ToolServerHandle {
crates/llm-worker/src/tool_server.rs:108: pub fn get_tool(&self, name: &str) -> Option<(ToolMeta, Arc<dyn Tool>)> {
crates/llm-worker/src/tool_server.rs:137: pub fn unregister(&self, name: &str) -> Result<(), ToolServerError> {
crates/llm-worker/src/tool_server.rs:150: pub fn replace(&self, factory: WorkerToolDefinition) -> Result<(), ToolServerError> {
crates/pod-registry/src/lifecycle.rs:18:pub struct ScopeAllocationGuard {
crates/pod-registry/src/lifecycle.rs:129:pub fn update_segment(pod_name: &str, new_segment_id: SegmentId) -> Result<(), ScopeLockError> {
crates/pod-registry/src/lifecycle.rs:164:pub fn lookup_segment(segment_id: SegmentId) -> Result<Option<SegmentLockInfo>, ScopeLockError> {
crates/llm-worker/src/callback.rs:191:pub struct ToolUseBlockScope {
crates/llm-worker/src/callback.rs:212: pub fn on_stop(&mut self, f: impl FnMut(&ToolCall) + Send + Sync + 'static) {
crates/memory/src/lib.rs:21:pub use error::{LintError, LintWarning, MemoryError};
crates/pod-registry/src/lib.rs:28:pub use error::ScopeLockError;
crates/llm-worker/examples/record_test_fixtures/recorder.rs:23:pub struct SessionMetadata {
crates/pod-registry/src/mutate.rs:161:pub fn release_pod(guard: &mut LockFileGuard, pod_name: &str) -> Result<(), ScopeLockError> {
crates/llm-worker/src/timeline/tool_call_collector.rs:30:pub struct ToolCallCollector {
crates/llm-worker/src/timeline/tool_call_collector.rs:44: pub fn take_collected(&self) -> Vec<ToolCall> {
crates/llm-worker/src/timeline/tool_call_collector.rs:50: pub fn collected(&self) -> Vec<ToolCall> {
crates/pod-registry/src/conflict.rs:50:pub fn is_within_effective_write(lock: &LockFile, parent: &str, rule: &ScopeRule) -> bool {
crates/memory/src/extract/tool.rs:92:pub fn write_extracted_tool(ctx: Arc<ExtractWorkerContext>) -> ToolDefinition {
crates/llm-worker/src/timeline/mod.rs:23:pub use tool_call_collector::ToolCallCollector;
crates/workflow/src/skill.rs:74: pub fn into_workflow_record(self, source: WorkflowSource) -> WorkflowRecord {
crates/llm-worker/src/handler.rs:158:pub struct ToolUseBlockKind;
crates/llm-worker/src/handler.rs:165:pub enum ToolUseBlockEvent {
crates/llm-worker/src/handler.rs:173:pub struct ToolUseBlockStart {
crates/llm-worker/src/handler.rs:180:pub struct ToolUseBlockStop {
crates/tui/src/input.rs:63:pub struct WorkflowInvokeAtom {
crates/workflow/src/schema.rs:12:pub struct WorkflowFrontmatter {
crates/workflow/src/schema.rs:45:pub fn split_frontmatter(content: &str) -> Result<(&str, &str), WorkflowLintError> {
crates/client/src/lib.rs:14:pub use pod_client::PodClient;
crates/workflow/src/workflow.rs:29:pub enum WorkflowSource {
crates/workflow/src/workflow.rs:50:pub struct WorkflowRecord {
crates/workflow/src/workflow.rs:93:pub struct WorkflowRegistry {
crates/workflow/src/workflow.rs:110: pub fn get(&self, slug: &Slug) -> Option<&WorkflowRecord> {
crates/workflow/src/workflow.rs:114: pub fn iter(&self) -> impl Iterator<Item = &WorkflowRecord> {
crates/workflow/src/workflow.rs:143: pub fn merge_skill(&mut self, record: WorkflowRecord) -> Option<ShadowedSkill> {
crates/workflow/src/workflow.rs:165:pub enum WorkflowLoadError {
crates/workflow/src/workflow.rs:191:pub fn load_workflows(layout: &WorkspaceLayout) -> Result<WorkflowRegistry, WorkflowLoadError> {
crates/client/src/pod_client.rs:9:pub struct PodClient {
crates/workflow/src/scope.rs:10:pub fn deny_write_rules(layout: &WorkspaceLayout) -> Vec<ScopeRule> {
crates/tui/src/block.rs:98:pub struct ToolCallBlock {
crates/tui/src/block.rs:113:pub enum ToolCallState {
crates/workflow/src/error.rs:10:pub enum WorkflowLintError {
crates/memory/src/workspace.rs:90: pub fn resolve(cfg: &manifest::MemoryConfig, default_root: &Path) -> Self {
crates/workflow/src/lib.rs:10:pub use error::WorkflowLintError;
crates/workflow/src/lib.rs:12:pub use linter::{WorkflowLintReport, WorkflowLinter};
crates/workflow/src/lib.rs:13:pub use schema::{WorkflowFrontmatter, split_frontmatter};
crates/workflow/src/linter.rs:15:pub struct WorkflowLintReport {
crates/workflow/src/linter.rs:24: pub fn push_error(&mut self, err: WorkflowLintError) {
crates/workflow/src/linter.rs:30:pub struct WorkflowLinter {
crates/workflow/src/linter.rs:47: pub fn lint(&self, content: &str) -> WorkflowLintReport {
crates/tui/src/tool.rs:22:pub struct ToolRenderOutput {
crates/tui/src/app.rs:193: pub fn set_pod_status(&mut self, status: PodStatus) {
crates/tools/src/task.rs:464:pub fn task_tools(store: TaskStore) -> Vec<ToolDefinition> {
crates/tools/src/bash.rs:330:pub fn bash_tool(fs: ScopedFs, output_dir: PathBuf) -> ToolDefinition {
crates/llm-worker/src/llm_client/scheme/openai_chat/events.rs:68: pub fn parse_event(&self, data: &str) -> Result<Option<Vec<Event>>, ClientError> {
crates/manifest/src/scope.rs:22:pub struct Scope {
crates/manifest/src/scope.rs:37:pub enum ScopeError {
crates/manifest/src/scope.rs:58: pub fn from_config(config: &ScopeConfig) -> Result<Self, ScopeError> {
crates/manifest/src/scope.rs:151: pub fn allow_rules(&self) -> Vec<ScopeRule> {
crates/manifest/src/scope.rs:168: pub fn deny_rules(&self) -> Vec<ScopeRule> {
crates/manifest/src/scope.rs:320: pub fn new(scope: Scope) -> Self {
crates/manifest/src/scope.rs:331: pub fn load(&self) -> Guard<Arc<Scope>> {
crates/manifest/src/scope.rs:338: pub fn snapshot(&self) -> Arc<Scope> {
crates/manifest/src/scope.rs:347: pub fn update<F>(&self, f: F) -> Result<(), ScopeError>
crates/tools/src/lib.rs:35:pub use error::ToolsError;
crates/tools/src/lib.rs:39:pub use scoped_fs::ScopedFs;
crates/tools/src/tracker.rs:116: pub fn verify(&self, path: &Path, current_bytes: &[u8]) -> Result<(), ToolsError> {
crates/llm-worker/src/llm_client/types.rs:573: pub fn tool(mut self, tool: ToolDefinition) -> Self {
crates/llm-worker/src/llm_client/types.rs:638:pub struct ToolDefinition {
crates/tools/src/read.rs:117:pub fn read_tool(fs: ScopedFs, tracker: Tracker) -> ToolDefinition {
crates/tools/src/glob.rs:196:pub fn glob_tool(fs: ScopedFs) -> ToolDefinition {
crates/tools/src/grep.rs:106:pub fn grep_tool(fs: ScopedFs) -> ToolDefinition {
crates/llm-worker/src/llm_client/client.rs:39:pub type ResponseStream = Pin<Box<dyn Stream<Item = Result<Event, ClientError>> + Send>>;
crates/tools/src/write.rs:78:pub fn write_tool(fs: ScopedFs, tracker: Tracker) -> ToolDefinition {
crates/manifest/src/lib.rs:19:pub use protocol::{Permission, ScopeRule};
crates/manifest/src/lib.rs:20:pub use scope::{Scope, ScopeError, SharedScope};
crates/manifest/src/lib.rs:35:pub struct PodManifest {
crates/manifest/src/lib.rs:90:pub struct MemoryConfig {
crates/manifest/src/lib.rs:153:pub struct PodMeta {
crates/manifest/src/lib.rs:223:pub struct ToolOutputLimits {
crates/manifest/src/lib.rs:295:pub struct ScopeConfig {
crates/manifest/src/lib.rs:307:pub struct SessionConfig {
crates/manifest/src/lib.rs:320:pub struct ToolPermissionConfig {
crates/manifest/src/lib.rs:328:pub struct ToolPermissionRule {
crates/manifest/src/lib.rs:341:pub enum ToolPermissionAction {
crates/tools/src/edit.rs:137:pub fn edit_tool(fs: ScopedFs, tracker: Tracker) -> ToolDefinition {
crates/tools/src/error.rs:12:pub enum ToolsError {
crates/tools/src/scoped_fs.rs:34:pub struct ScopedFs {
crates/tools/src/scoped_fs.rs:67: pub fn new(scope: Scope, pwd: PathBuf) -> Self {
crates/tools/src/scoped_fs.rs:83: pub fn scope(&self) -> Arc<Scope> {
crates/tools/src/scoped_fs.rs:108: pub fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, ToolsError> {
crates/tools/src/scoped_fs.rs:160: pub fn write(&self, path: &Path, content: &[u8]) -> Result<WriteOutcome, ToolsError> {
crates/manifest/src/config.rs:28:pub struct PodManifestConfig {
crates/manifest/src/config.rs:58:pub struct PodMetaConfig {
crates/manifest/src/config.rs:95:pub struct ToolOutputLimitsPartial {
crates/manifest/src/config.rs:109:pub struct SessionConfigPartial {
crates/manifest/src/config.rs:282: pub fn merge(self, upper: PodManifestConfig) -> Self {
crates/pod/src/controller.rs:34:pub struct PodHandle {
crates/pod/src/controller.rs:130:pub struct PodController;
crates/protocol/src/lib.rs:77:pub enum PodEvent {
crates/protocol/src/lib.rs:492:pub struct MemoryWorkerEvent {
crates/protocol/src/lib.rs:575:pub enum PodStatus {
crates/protocol/src/lib.rs:649:pub struct ScopeRule {
crates/manifest/src/cascade.rs:63:pub fn load_layer(path: &Path) -> Result<PodManifestConfig, LayerLoadError> {
crates/pod/src/ipc/event.rs:46:pub fn fire_and_forget(socket: Option<PathBuf>, event: PodEvent) {
crates/pod/src/ipc/event.rs:60:pub fn render_event(event: &PodEvent) -> String {
crates/pod/src/lib.rs:20:pub use controller::{PodController, PodHandle, ShutdownReceiver};
crates/pod/src/lib.rs:21:pub use factory::{FactoryError, PodFactory};
crates/pod/src/lib.rs:28:pub use pod::{Pod, PodError, PodRunResult, apply_worker_manifest};
crates/pod/src/lib.rs:29:pub use prompt::catalog::{CatalogError, PodPrompt, PromptCatalog};
crates/pod/src/lib.rs:32:pub use protocol::{ErrorCode, Event, Method, PodStatus, TurnResult};
crates/pod/src/lib.rs:36:pub use shared_state::PodSharedState;
crates/pod/src/ipc/notify_buffer.rs:68: pub fn push_pod_event(&self, event: PodEvent) {
crates/pod/src/shared_state.rs:10:pub struct WorkflowCandidate {
crates/pod/src/shared_state.rs:29:pub struct PodSharedState {
crates/pod/src/shared_state.rs:67: pub fn set_fs_view(&self, view: PodFsView) {
crates/pod/src/shared_state.rs:73: pub fn fs_view(&self) -> Option<&PodFsView> {
crates/pod/src/shared_state.rs:77: pub fn set_workflows(&self, workflows: Vec<WorkflowCandidate>) {
crates/pod/src/shared_state.rs:81: pub fn list_workflow_completions(&self, prefix: &str) -> Vec<WorkflowCandidate> {
crates/pod/src/shared_state.rs:111: pub fn set_status(&self, status: PodStatus) {
crates/pod/src/shared_state.rs:117: pub fn get_status(&self) -> PodStatus {
crates/pod/src/pod.rs:86: pub fn new(session_id: SessionId, segment_id: SegmentId, entries_written: usize) -> Arc<Self> {
crates/pod/src/pod.rs:100: pub fn session_id(&self) -> SessionId {
crates/pod/src/pod.rs:222:pub struct Pod<C: LlmClient, St: Store> {
crates/pod/src/pod.rs:690: pub fn session_id(&self) -> SessionId {
crates/pod/src/pod.rs:695: pub fn manifest(&self) -> &PodManifest {
crates/pod/src/pod.rs:713: pub fn scope_snapshot(&self) -> Arc<Scope> {
crates/pod/src/pod.rs:791: pub fn scope_change_sink(&self) -> Arc<dyn Fn(PodScopeSnapshot) + Send + Sync> {
crates/pod/src/pod.rs:1056: pub fn push_pod_event_notify(&self, event: protocol::PodEvent) {
crates/pod/src/pod.rs:4061:pub enum PodRunResult {
crates/pod/src/pod.rs:4332:pub enum PodError {
crates/pod/src/discovery.rs:33:pub struct PodDiscovery<St> {
crates/pod/src/discovery.rs:368:pub enum PodStateStatus {
crates/pod/src/discovery.rs:441:pub struct PodDetail {
crates/pod/src/discovery.rs:482:pub enum PodDiscoveryError {
crates/pod/src/discovery.rs:678:pub fn list_visible_pods_tool<St>(discovery: PodDiscovery<St>) -> ToolDefinition
crates/pod/src/discovery.rs:699:pub fn inspect_pod_tool<St>(discovery: PodDiscovery<St>) -> ToolDefinition
crates/pod/src/discovery.rs:716:pub fn attach_or_restore_pod_tool<St>(discovery: PodDiscovery<St>) -> ToolDefinition
crates/pod/src/factory.rs:76:pub struct PodFactory {
crates/pod/src/factory.rs:189: pub fn with_overlay_config(mut self, config: PodManifestConfig) -> Result<Self, FactoryError> {
crates/pod/src/factory.rs:241: pub fn resolve(self) -> Result<(PodManifest, PromptLoader), FactoryError> {
crates/pod/src/hook.rs:75:pub struct ToolCallSummary {
crates/pod/src/hook.rs:90:pub struct ToolResultSummary {
crates/pod/src/fs_view.rs:40:pub struct PodFsView {
crates/pod/src/fs_view.rs:77: pub fn new(fs: ScopedFs) -> Self {
crates/pod/src/fs_view.rs:81: pub fn fs(&self) -> &ScopedFs {
crates/pod/src/workflow/mod.rs:17:pub enum WorkflowResolveError {
crates/pod/src/prompt/catalog.rs:61:pub enum PodPrompt {
crates/pod/src/prompt/catalog.rs:303: pub fn render(&self, prompt: PodPrompt, ctx: Value) -> Result<String, CatalogError> {
crates/pod/src/spawn/comm_tools.rs:94:pub fn send_to_pod_tool(registry: Arc<SpawnedPodRegistry>) -> ToolDefinition {
crates/pod/src/spawn/comm_tools.rs:169:pub fn read_pod_output_tool(registry: Arc<SpawnedPodRegistry>) -> ToolDefinition {
crates/pod/src/spawn/comm_tools.rs:229:pub fn stop_pod_tool(registry: Arc<SpawnedPodRegistry>) -> ToolDefinition {
crates/pod/src/spawn/comm_tools.rs:299:pub fn list_pods_tool(registry: Arc<SpawnedPodRegistry>) -> ToolDefinition {
@@ -0,0 +1,16 @@
1 client <- tui
2 daemon <-
3 lint-common <- memory, workflow
4 llm-worker <- manifest, memory, pod, provider, session-store, tools, tui
5 llm-worker-macros <- llm-worker
6 manifest <- client, daemon, memory, pod, pod-registry, provider, tools, tui, workflow
7 memory <- pod, workflow
8 pod <-
9 pod-registry <- pod, tui
10 protocol <- client, daemon, manifest, pod, session-store, tui
11 provider <- pod
12 session-metrics <- pod
13 session-store <- pod, pod-registry, session-metrics, tui
14 tools <- pod, tui
15 tui <-
16 workflow <- pod
@@ -0,0 +1,16 @@
client <- tui
daemon <-
lint-common <- memory, workflow
llm-worker <- manifest, memory, pod, provider, session-store, tools, tui
llm-worker-macros <- llm-worker
manifest <- client, daemon, memory, pod, pod-registry, provider, tools, tui, workflow
memory <- pod, workflow
pod <-
pod-registry <- pod, tui
protocol <- client, daemon, manifest, pod, session-store, tui
provider <- pod
session-metrics <- pod
session-store <- pod, pod-registry, session-metrics, tui
tools <- pod, tui
tui <-
workflow <- pod
@@ -0,0 +1,35 @@
crates/protocol/src/lib.rs:68:/// parent Controller applies variant-specific side effects (registry /
crates/protocol/src/lib.rs:118:/// `Segment::Text`; richer clients (TUI / GUI) construct typed atoms
crates/protocol/src/lib.rs:120:/// send them through directly so the Pod side never has to re-parse a
crates/protocol/src/lib.rs:124:/// `Segment::Unknown`. Pod treats this the same as known-but-unresolved
crates/protocol/src/lib.rs:152: /// Unknown variant from a newer client. Pod treats this as an
crates/protocol/src/lib.rs:224: /// additional TUI / GUI instances show the same pending user line
crates/protocol/src/lib.rs:237: /// Carries the JSON form of `session_store::SystemItem`. Covers
crates/protocol/src/lib.rs:394: /// as the JSON form of `session_store::LogEntry`. This is the
crates/protocol/src/lib.rs:419: /// Payload is the JSON form of `session_store::LogEntry::SegmentStart`.
crates/protocol/src/lib.rs:459: /// `CompactDone` (with the new `SegmentId`); failure by `CompactFailed`.
crates/llm-worker/src/llm_client/types.rs:523: /// 会話単位の安定キー。`prompt_cache_key` として送られる
crates/llm-worker/src/llm_client/types.rs:526: /// ほぼヒットしないため、pod 側で `SegmentId` を渡す運用を想定。
crates/llm-worker/src/llm_client/types.rs:529: /// `prompt_cache_key` を持たない provider は無視する。
crates/session-store/src/segment.rs:11:use crate::{SegmentId, SessionId};
crates/session-store/src/segment.rs:29:) -> Result<(SessionId, SegmentId), StoreError> {
crates/session-store/src/segment.rs:44: segment_id: SegmentId,
crates/session-store/src/segment.rs:69: source_segment_id: SegmentId,
crates/session-store/src/segment.rs:71:) -> Result<SegmentId, StoreError> {
crates/session-store/src/segment.rs:96: segment_id: SegmentId,
crates/session-store/src/segment.rs:109: segment_id: SegmentId,
crates/session-store/src/segment.rs:146: segment_id: &mut SegmentId,
crates/session-store/src/segment.rs:184: segment_id: SegmentId,
crates/session-store/src/segment.rs:209: segment_id: SegmentId,
crates/session-store/src/segment.rs:258: segment_id: SegmentId,
crates/session-store/src/segment.rs:276: segment_id: SegmentId,
crates/session-store/src/segment.rs:294: segment_id: SegmentId,
crates/session-store/src/segment.rs:317: segment_id: SegmentId,
crates/session-store/src/segment.rs:342: segment_id: SegmentId,
crates/session-store/src/segment.rs:372: segment_id: SegmentId,
crates/session-store/src/segment.rs:392: segment_id: SegmentId,
crates/session-store/src/segment.rs:409: segment_id: SegmentId,
crates/session-store/src/segment.rs:432:) -> Result<(SessionId, SegmentId), StoreError> {
crates/session-store/src/segment.rs:466: source_id: SegmentId,
crates/session-store/src/segment.rs:468:) -> Result<SegmentId, StoreError> {
crates/session-store/src/segment.rs:511: segment_id: SegmentId,
@@ -0,0 +1,47 @@
---
id: 20260528-131317-crate-boundary-audit
slug: crate-boundary-audit
title: Audit crate responsibility boundaries
status: open
kind: audit
priority: P2
labels: [architecture, crates]
created_at: 2026-05-28T13:13:17Z
updated_at: 2026-05-28T13:13:17Z
assignee: null
legacy_ticket: null
---
## Background
The workspace has grown across multiple crates (`pod`, `protocol`, `llm-worker`, `manifest`, `client`, `tui`, `memory`, `workflow`, etc.). Before adding more orchestration and policy features, audit whether crate responsibilities, dependency direction, and public interfaces are still clean.
This is an architecture audit, not an implementation ticket. The output should be actionable findings: either concrete boundary violations to fix, or an explicit statement that the inspected area is acceptable.
The audit must also check code comments and documentation comments. Comments inside one crate should not explain or justify behavior primarily in terms of a downstream crate that depends on it. If such comments exist, record them because they can indicate inverted ownership or an interface that is leaking caller-specific concerns.
## Scope
Inspect the Rust workspace at least for:
- crate dependency graph and suspicious dependency direction.
- public types/functions/modules whose names or contracts expose another crate's implementation details unnecessarily.
- code paths where a lower-level crate appears to know about higher-level orchestration, UI, or caller concerns.
- comments/doc-comments that mention another crate which depends on the current crate, especially when the comment describes why the dependent crate needs that behavior.
- duplicated interfaces or ad-hoc glue that should be owned by a clearer boundary.
Out of scope:
- broad refactoring.
- formatting-only changes.
- changing dependency direction before findings are reviewed.
- rewriting comments unless a follow-up implementation ticket is explicitly created.
## Acceptance criteria
- A dependency/interface audit summary exists with concrete findings grouped by severity.
- The audit names files/modules/functions/comments involved in each finding.
- The audit distinguishes actual boundary problems from acceptable dependency-aware documentation.
- The audit specifically reports whether comments in crates refer to crates that depend on them.
- If no blocking issue is found, the audit explains why the current separation is acceptable.
- Follow-up implementation tickets are proposed only for findings that are specific and actionable.
@@ -0,0 +1,7 @@
<!-- event: create author: tickets.sh at: 2026-05-28T13:13:17Z -->
## Created
Created by tickets.sh create.
---
@@ -0,0 +1,93 @@
---
id: 20260529-041911-llm-worker-standalone-publication-audit
slug: llm-worker-standalone-publication-audit
title: Prepare LLM-Worker for standalone publication
status: open
kind: audit
priority: P2
labels: [llm-worker, docs, api, release]
created_at: 2026-05-29T04:19:11Z
updated_at: 2026-05-29T04:19:11Z
assignee: null
legacy_ticket: null
---
## Background
`llm-worker` is currently developed as part of the Insomnia workspace, but it is intended to be useful as a standalone library for building autonomous LLM-powered systems.
Before publishing or presenting it independently, audit and polish the crate's public surface, documentation, examples, and wording so it can stand on its own without assuming Insomnia-specific context.
This is primarily an audit/preparation ticket. Implementation changes should be limited to documentation polish, small API text/name cleanups, metadata fixes, and clearly safe public-surface adjustments. If the audit finds larger API redesign needs, record them as follow-up tickets rather than mixing a broad refactor into this work.
## Scope
Inspect and update, at minimum:
- `crates/llm-worker/Cargo.toml`
- description
- version readiness
- license/categories/keywords/repository/readme/include/exclude metadata where appropriate
- dependency choices and feature flags that matter for standalone consumers
- `crates/llm-worker/README.md`
- standalone crate overview
- core concepts
- minimal usage example
- provider/model configuration assumptions
- tool/interceptor concepts
- streaming/retry/continuation limitations
- `crates/llm-worker/docs/*`
- architecture and requirements accuracy
- wording that assumes the full Insomnia application
- public Rust API docs
- crate-level docs (`lib.rs`)
- public structs/enums/functions/traits likely to appear in rustdoc
- examples should compile or be marked clearly as illustrative
- examples/tests under `crates/llm-worker`
- ensure they are understandable to external users
- avoid leaking local project assumptions or private operational names
## Audit questions
- Can a reader understand what `llm-worker` does without knowing Insomnia Pod/TUI internals?
- Is the public API coherent as a standalone library boundary?
- Are names and docs provider-neutral where possible?
- Are Insomnia-specific terms either absent from public docs or explicitly framed as one possible host application?
- Are error types, interceptors, tool traits, retry/continuation behavior, and history handling documented enough for external use?
- Does crate metadata look publishable?
- Are there obvious public APIs that should be hidden, renamed, or documented before publication?
## Requirements
- Produce a written audit summary in the ticket artifacts directory.
- Apply small documentation/metadata/API wording fixes that are clearly safe.
- Do not perform broad API redesign in this ticket.
- Do not change runtime behavior unless a tiny fix is required to make docs/examples accurate.
- If API redesign is needed, propose follow-up tickets with concrete scope.
- Keep Insomnia workspace behavior unchanged.
## Acceptance criteria
- `crates/llm-worker` has standalone-oriented README / docs wording.
- Crate metadata is reviewed and updated where appropriate.
- Public rustdoc entry points are checked for missing or Insomnia-specific wording.
- An audit artifact records:
- reviewed files/commands
- public API concerns
- docs wording concerns
- metadata/readiness concerns
- recommended follow-up tickets
- Any small fixes are committed with the audit.
- `cargo fmt --check`
- `cargo check -p llm-worker`
- Relevant `cargo test -p llm-worker` or focused tests/examples where practical.
- `cargo doc -p llm-worker --no-deps` succeeds or known warnings/issues are recorded.
## Out of scope
- Publishing to crates.io.
- Renaming the crate.
- Extracting the crate into a separate repository.
- Large public API redesign.
- Reworking provider implementations.
- Changing Insomnia Pod/TUI behavior.
@@ -0,0 +1,7 @@
<!-- event: create author: tickets.sh at: 2026-05-29T04:19:11Z -->
## Created
Created by tickets.sh create.
---
@@ -0,0 +1,91 @@
---
id: 20260529-161928-mcp-integration
slug: mcp-integration
title: MCP integration as external tool/resource/prompt provider
status: open
kind: feature
priority: P2
labels: [mcp, tools, security, profiles]
created_at: 2026-05-29T16:19:28Z
updated_at: 2026-05-29T16:19:28Z
assignee: null
legacy_ticket: null
---
## Background
MCP (Model Context Protocol) is an open JSON-RPC based protocol for connecting AI applications to external systems. MCP servers can expose tools, resources, and prompts; clients/hosts can also expose capabilities such as roots, elicitation, sampling, logging, progress, and cancellation. Common transports are local stdio and remote Streamable HTTP.
Insomnia already has a built-in tool registry, manifest/profile-driven policy, scoped filesystem permissions, prompt/workflow assets, and bounded tool output. MCP should integrate with those existing safety and orchestration layers rather than bypass them.
MCP servers must be treated as untrusted external capability providers. Tool descriptions, annotations, resource content, and prompt templates returned by a server are data from an external system and must not implicitly weaken Insomnia scope, permission, prompt-context, or history-persistence rules.
## Requirements
- Add MCP server configuration through the manifest/profile system.
- Profiles should be able to declare named MCP servers.
- Server configuration should support local stdio as the first transport.
- Streamable HTTP can be a later phase, but the design should not preclude it.
- Server process commands, arguments, environment/credential references, and working directory must be explicit.
- Implement MCP client foundation.
- JSON-RPC 2.0 message handling.
- lifecycle initialization and capability negotiation.
- `notifications/initialized` after successful initialization.
- graceful shutdown and clear diagnostics for startup/protocol failures.
- Bridge MCP tools into Insomnia's tool system.
- Use `tools/list` to discover tools.
- Register discovered MCP tools under stable names that include the server namespace.
- Execute tool calls through `tools/call`.
- Preserve existing PreToolCall permission policy and manifest/profile tool controls.
- Bound tool result size and serialize results without allowing server-provided data to act as instructions.
- Support `notifications/tools/list_changed` by refreshing the registered tool list when safe.
- Treat MCP resources and prompts as explicit context sources, not hidden injections.
- Initial implementation may defer resources/prompts, but the design must specify that `resources/read` and `prompts/get` are explicit user/model-visible operations with permission/policy gates.
- Do not silently inject resource or prompt content into LLM context outside history.
- Connect filesystem roots to Insomnia scope.
- If MCP roots are supported, expose only authorized scope roots.
- A server must not learn or operate on paths outside the configured scope.
- Keep client-side MCP capabilities conservative.
- Sampling is powerful and should be disabled initially or require explicit approval because it lets an MCP server request LLM completions.
- Elicitation should require an approval/UI path before a server can request user input.
- Logging/progress notifications should be surfaced as diagnostics without polluting model context.
- Security and trust constraints.
- Tool descriptions and schemas from MCP servers are untrusted metadata.
- All MCP tool invocations remain subject to Insomnia tool permission policy.
- Server-provided content must not override system/developer instructions.
- Secrets are passed only through explicit env/secret references and must not be logged or exposed to model context.
- Remote MCP servers require an explicit future design for auth, TLS, redirects, private network policy, and output limits.
- UX and observability.
- Startup failures should identify the MCP server and failing phase.
- Tool list changes and server disconnects should be visible to the user/TUI.
- Provide enough diagnostics for debugging without printing secrets.
- Documentation.
- Explain MCP's trust model in Insomnia.
- Show examples for local stdio MCP servers.
- Document how MCP tool names, permissions, scope, and profiles interact.
## Suggested implementation phases
1. stdio MCP client foundation with mock-server tests.
2. `tools/list` / `tools/call` bridge into the existing tool registry and permission policy.
3. Manifest/profile configuration and CLI/Pod startup integration.
4. TUI diagnostics for server startup/disconnect/tool-list changes.
5. Resources/prompts support as explicit operations.
6. Streamable HTTP transport and auth design.
7. Sampling/elicitation only after an approval/resume protocol exists.
## Acceptance criteria
- A manifest/profile can configure at least one local stdio MCP server.
- Pod startup initializes the configured MCP server and reports clear diagnostics on failure.
- `tools/list` results are registered as Insomnia tools with namespaced stable names.
- Calling a registered MCP tool invokes `tools/call` and returns bounded structured output to the model.
- Existing tool permission policy applies to MCP tools before execution.
- MCP server metadata/content is treated as untrusted and does not bypass prompt-context or history-persistence rules.
- Secrets used by MCP server configuration are represented as explicit env/secret references and are not logged or serialized as plaintext.
- Filesystem roots, if exposed, are derived from authorized Insomnia scope only.
- Sampling and elicitation are disabled or fail closed unless an explicit approval path is implemented.
- Focused tests cover protocol lifecycle, tool discovery, tool call success/failure, permission denial, bounded output, server disconnect, and no-secret diagnostics.
- Documentation includes a local stdio MCP server example and security guidance.
- `cargo fmt --check`
- Relevant manifest/tools/pod/tui tests pass.
@@ -0,0 +1,7 @@
<!-- event: create author: tickets.sh at: 2026-05-29T16:19:28Z -->
## Created
Created by tickets.sh create.
---
@@ -0,0 +1,74 @@
---
id: 20260530-053721-tui-inflight-composer-injection
slug: tui-inflight-composer-injection
title: Support immediate in-flight TUI composer injection
status: open
kind: feature
priority: P2
labels: [tui, worker, interrupt, ux]
created_at: 2026-05-30T05:37:21Z
updated_at: 2026-05-30T05:38:11Z
assignee: null
legacy_ticket: null
---
## Background
The TUI currently lets the user press Enter while a Pod is executing, but that input is queued for the next turn. This is useful when the user wants to continue the task after the current run finishes.
There is a separate UX need: while the model is in the middle of a long run with tool calls, the user may want to send urgent supplemental context that should be seen as soon as possible, ideally between tool calls / LLM calls during the current run. This is different from ordinary queued input.
We want both modes:
- **after-run queue**: “when this task finishes, continue with this next request.”
- **in-flight injection**: “while you are still working, please incorporate this additional context as soon as safe.”
This ticket is for designing and implementing an explicit TUI path for the second mode without breaking the existing queued-input behavior.
## Requirements
- Preserve the current Enter-while-running behavior as the after-run queue.
- Add an explicit user action / keybinding / command for immediate in-flight injection while a run is active.
- In-flight injected text must be delivered through the Pod/Worker history path, not as hidden context-only injection. It must satisfy the project principle that new input placed into LLM context is first appended to `worker.history` / persisted history.
- In-flight injection should be consumed at safe boundaries, such as before the next LLM request or between tool-call cycles, not by mutating an already-open provider stream.
- The UI must make the distinction visible: queued-for-next-turn vs injected-into-current-run.
- If no run is active, the immediate-injection action should either behave like normal submit or clearly report that there is no in-flight run to inject into.
- If the current turn cannot accept in-flight input at a safe boundary, the UI should fail closed or fall back to explicit queued mode with a visible notice; do not silently drop input.
- Preserve TUI-local input history behavior for submitted/queued text.
## Non-goals
- Do not interrupt/cancel the current run as part of this ticket.
- Do not mutate provider streams already in progress.
- Do not introduce hidden system-reminder/context-only messages that are not recorded in history.
- Do not remove the existing queued composer behavior.
- Do not redesign the entire Pod notification/input protocol unless a small typed Method/Event extension is required.
## Open design questions
- What should the TUI action be?
- Separate command such as `:inject`?
- Modified Enter keybinding such as Ctrl+Enter / Alt+Enter?
- Action menu entry?
- What Pod protocol shape is best?
- Existing `Method::Notify` may already represent in-flight user-visible context, but semantics must be checked.
- A new typed method such as `Method::InjectInput` may be clearer if `Notify` is too generic.
- What history item should represent the injected text?
- User item?
- System item with user-originated note?
- Existing Notify / PodEvent item?
- What exact safe boundaries are supported in `Worker` / controller today?
- before the next LLM request;
- before resuming after tool results;
- while a tool call is running;
- while provider stream is open.
- How should the UI display pending in-flight injection versus after-run queue?
## Acceptance criteria
- TUI users can choose between after-run queued submit and immediate in-flight injection while a Pod is running.
- In-flight injected input is recorded in history before it can influence an LLM request.
- In-flight injection is consumed only at safe boundaries and never mutates an active provider stream.
- The TUI visibly distinguishes queued-next-turn input from injected-current-run input.
- Existing queued Enter behavior remains intact.
- Tests cover TUI input routing, protocol/controller handling, worker history append behavior, and safe-boundary behavior.
@@ -0,0 +1,33 @@
<!-- event: create author: tickets.sh at: 2026-05-30T05:37:21Z -->
## Created
Created by tickets.sh create.
---
<!-- event: plan author: hare at: 2026-05-30T05:38:11Z -->
## Plan
## Initial preflight
Classification: requirements-sync-needed.
The user requirement is clear at the UX level: Enter while running remains an after-run queue, and a separate action should inject supplemental context into the current in-flight run as soon as safe. The exact protocol/history representation is not decided yet and must be designed before implementation.
Critical constraints:
- Do not place injected text into LLM context unless it has first been appended to Worker history / persisted history.
- Do not mutate an active provider stream.
- Consume injected text only at safe boundaries such as before a later LLM request or between tool-call cycles.
- Do not silently drop text; if the active turn cannot accept injection, report/fail closed or explicitly queue.
Design questions to settle before coding:
- TUI action/keybinding/command name.
- Whether existing `Method::Notify` is semantically sufficient or a new typed method is needed.
- Which history item represents user-originated in-flight supplemental context.
- Which Worker/controller boundaries can actually observe injected input before the next LLM call.
- How queued-next-turn vs injected-current-run is displayed.
---
@@ -0,0 +1,10 @@
# Decision: split feature registry and Hook hardening from Plugin architecture
The Plugin architecture ticket remains the broad architecture surface for Tools, Hooks, runtime kinds, capability model, trust model, discovery/enablement, and MCP/WASM/declarative runtime mapping.
Two implementation-oriented prerequisite tickets are split out:
- `plugin-feature-contribution-registry`: define and implement the Pod-layer feature contribution registry so built-in and future external capabilities register through existing Tool / Hook / Notify paths instead of ad hoc Pod code paths.
- `hook-public-surface-hardening`: audit and harden `pod::hook` before exposing it as a feature/plugin contribution boundary, especially removing public access to raw internal action types that can inject model-visible `Item` values.
This preserves the desired detachable shape: feature state remains in the feature/extension module, while Pod interaction happens through existing durable host surfaces. WorkItem management should be implemented as a built-in feature contribution once the registry boundary is in place, rather than as a special Pod context-injection path.
@@ -0,0 +1,89 @@
---
id: 20260531-010005-plugin-extension-surface
slug: plugin-extension-surface
title: Plugin: define extension surface for hooks and tools
status: open
kind: feature
priority: P2
labels: [plugin, hooks, tools, wasm, mcp]
created_at: 2026-05-31T01:00:05Z
updated_at: 2026-06-03T12:25:05Z
assignee: null
legacy_ticket: null
---
## Background
insomnia currently has internal Hook / Tool concepts, plus a separate planned MCP integration ticket (`mcp-integration`). The next design step is to define the project-level Plugin surface: how user/project-provided extensions can contribute Tools and Hooks without weakening scope, permission, history, or prompt-context invariants.
The plugin surface should not be a grab bag of arbitrary code execution. Candidate extension mechanisms have different trust and protocol properties:
- MCP: protocol-bound external tool/resource/prompt provider surface.
- TOML/config-only hooks: declarative configuration for simple hook behavior without arbitrary code.
- WASM: planned first programmable plugin runtime for Hooks and Tools, with explicit capability imports and sandboxing.
- General scripting languages: considered, but not the initial direction because arbitrary script execution broadens the trust/runtime surface too quickly.
## Related work
- `work-items/open/20260529-161928-mcp-integration/` — MCP integration as one plugin backend / external capability bridge.
- `work-items/open/20260603-122317-plugin-feature-contribution-registry/` — implementation-oriented runtime registry split-out for built-in and external feature contributions.
- `work-items/open/20260603-122317-hook-public-surface-hardening/` — prerequisite hardening for public Hook contribution safety.
- Existing internal hooks/tools code: `crates/pod`, `crates/tools`, `crates/llm-worker`.
- Manifest permission policy and scope enforcement must remain authoritative for plugin-provided tools.
## Requirements
- Define a Plugin surface that can provide:
- Tools callable by the LLM through the normal ToolRegistry / permission / scope path;
- Hooks observing or influencing Pod/Worker lifecycle through the existing Hook boundary, not by directly mutating worker history/context.
- Separate plugin description/registration from plugin runtime implementation.
- A plugin manifest should declare provided tools/hooks, required capabilities, configuration schema or config values, and trust/runtime type.
- Runtime implementations can include MCP, declarative config hooks, and WASM in separate phases.
- Keep MCP as a related backend, not the whole plugin model.
- MCP servers remain untrusted external capability providers bridged through allowlists, bounded output, scope/permission policy, and explicit resource/prompt use.
- Define a declarative hook path for simple TOML/config-only behavior where code execution is unnecessary.
- Define a WASM plugin direction for programmable Hooks/Tools.
- WASM modules must receive explicit host imports/capabilities only.
- File/network/process access must not be ambient; all external effects go through host-provided capability APIs and existing policy checks.
- Tool outputs must be bounded and recorded through normal history/tool-result paths.
- Preserve LLM context/history invariants.
- Plugins must not inject cross-turn invisible context.
- If plugin output becomes model-visible, it must enter through durable history/tool/hook paths according to existing rules.
- Preserve scope and permission invariants.
- Plugin-provided tools must not bypass `ScopedFs`, manifest tool permission policy, child scope delegation, or web/network policy.
- Clarify trust model and lifecycle.
- Builtin vs project vs user plugins.
- Discovery/enablement through manifest/profile/config.
- Versioning / compatibility boundaries.
- Diagnostics when a plugin cannot load or asks for unavailable capabilities.
## Non-goals
- Implementing the full WASM runtime in the first design step.
- Implementing MCP itself beyond referencing the existing MCP integration ticket.
- Supporting arbitrary host scripting languages as a first-class plugin runtime.
- Allowing plugins to mutate session history, memory, prompt context, or scope outside approved APIs.
- Adding UI plugin systems or TUI rendering extensions.
## Suggested phases
1. **Design / architecture note**
- Define Plugin, PluginManifest, PluginRuntimeKind, Tool contribution, Hook contribution, capability request, and trust/source model.
- Map MCP, declarative hooks, and WASM onto that model.
2. **Internal registry boundary**
- Detailed implementation is split to `plugin-feature-contribution-registry` so this ticket can stay focused on the architecture surface and invariants.
3. **Declarative hooks MVP**
- Add a non-code configuration path for simple hook behavior if an immediate use case exists.
4. **WASM spike**
- Evaluate runtime (`wasmtime` or alternative), host imports, resource limits, serialization, and Nix/package impact.
5. **MCP bridge alignment**
- Ensure `mcp-integration` plugs into the same Tool/permission/output boundary rather than becoming a parallel extension path.
## Acceptance criteria
- The repository has a documented plugin architecture proposal covering Tools, Hooks, runtimes, capability model, trust model, and discovery/enablement.
- MCP is positioned as one plugin backend / bridge and linked to `mcp-integration`, not treated as the only extension mechanism.
- The proposal explicitly explains why arbitrary scripting languages are deferred and why WASM is the initial programmable runtime direction.
- The design preserves existing scope, permission, history, and prompt-context invariants.
- Follow-up implementation tickets can be cut independently for declarative hooks, WASM runtime, and MCP bridge integration.
- Any code changes in this ticket, if taken beyond design docs, are limited to safe internal boundaries and have focused tests.
@@ -0,0 +1,42 @@
<!-- event: create author: tickets.sh at: 2026-05-31T01:00:05Z -->
## Created
Created by tickets.sh create.
---
<!-- event: decision author: hare at: 2026-05-31T01:01:09Z -->
## Decision
Initial decision note from user discussion:
- The plugin surface should mainly expose Hooks and Tools.
- MCP is related, but should be treated as one protocol-bound backend/bridge rather than the entire plugin model.
- Planned plugin mechanisms:
- MCP for protocol-constrained external capability providers;
- TOML/config-only hooks for simple behavior that does not need arbitrary code;
- WASM for programmable Hooks/Tools with explicit host capabilities.
- General scripting languages were considered, but the initial direction is WASM because it offers a clearer sandbox/capability boundary.
---
<!-- event: decision author: hare at: 2026-06-03T12:25:05Z -->
## Decision
# Decision: split feature registry and Hook hardening from Plugin architecture
The Plugin architecture ticket remains the broad architecture surface for Tools, Hooks, runtime kinds, capability model, trust model, discovery/enablement, and MCP/WASM/declarative runtime mapping.
Two implementation-oriented prerequisite tickets are split out:
- `plugin-feature-contribution-registry`: define and implement the Pod-layer feature contribution registry so built-in and future external capabilities register through existing Tool / Hook / Notify paths instead of ad hoc Pod code paths.
- `hook-public-surface-hardening`: audit and harden `pod::hook` before exposing it as a feature/plugin contribution boundary, especially removing public access to raw internal action types that can inject model-visible `Item` values.
This preserves the desired detachable shape: feature state remains in the feature/extension module, while Pod interaction happens through existing durable host surfaces. WorkItem management should be implemented as a built-in feature contribution once the registry boundary is in place, rather than as a special Pod context-injection path.
---
@@ -0,0 +1,56 @@
---
id: 20260601-021104-tui-composer-history-persistence
slug: tui-composer-history-persistence
title: TUI: persist composer input recall history per workspace
status: open
kind: task
priority: P2
labels: [tui, composer, history, persistence]
created_at: 2026-06-01T02:11:04Z
updated_at: 2026-06-01T02:11:04Z
assignee: null
legacy_ticket: null
---
## Issue
TUI composer では上下キーで過去に送信・queue した入力を recall できるが、現在の履歴は TUI-local な揮発状態に留まっている。新しく TUI を起動し直した後も、同じ workspace で使った composer input history を呼び出せるようにしたい。
既存決定として、composer input history recall は Pod protocol / transcript / session history を変更しない TUI-local editing affordance である。この意味論は維持したまま、保存先だけを user data として永続化する。
## Storage decision
永続化先は workspace 配下の `./.insomnia/` ではなく、ユーザー data dir(既定では `~/.insomnia`、実装上は既存の data-dir 解決に従う)を使う方針とする。
理由:
- composer input history は個人の操作履歴であり、project-authored asset ではない。
- 入力には secret / private context が混ざり得るため、workspace に書くと git 追跡・共有・公開監査のリスクが上がる。
- `./.insomnia/` は workflow / knowledge / manifest assets など project/workspace 側の明示的な資産に寄せ、生成された個人履歴は user data 側に置く方が境界が明確。
- 「どの workspace の履歴か」は、user data 側で workspace identitycanonical workspace root / git root などから作る stable key)と display metadata を持てば表現できる。
## Requirements
- 上下キーで呼び出す composer input history を、TUI 再起動後も利用できるよう永続化すること。
- 履歴は workspace ごとに分離すること。別 workspace の入力履歴が通常操作で混ざらないこと。
- 保存先は既存の user data dir 配下にすること。デフォルト表示としては `~/.insomnia` 相当だが、実装は data-dir override / path resolution に従うこと。
- `./.insomnia/` には composer history を作成しないこと。
- 保存 record は、どの workspace の履歴か判別できる metadata / key を持つこと。
- 既存の TUI-local / non-destructive recall semantics を維持すること。
- Pod protocol を変えない。
- transcript / session history を mutate しない。
- recalled input は、ユーザーが再送信するまでは conversation state に影響しない。
- 入力は typed `Segment` vector として保存し、structured input の recall を壊さないこと。
- non-blank input のみ保存し、連続重複を抑止し、履歴件数は bounded にすること。既存挙動の 100 件 bound を尊重する。
- secret 値が混ざり得る前提で、保存内容を diagnostics / logs / tickets / tests snapshot / model context に不用意に出さないこと。
- 破損した履歴ファイルがあっても TUI startup を致命的に壊さず、必要なら warning と空履歴 fallback にすること。
- 既存の multiline cursor navigation、Up/Down browse、draft restore、edit-on-recall の挙動を regression させないこと。
## Acceptance criteria
- 同じ workspace で TUI を再起動しても、以前送信した composer input を上下キーで recall できる。
- 別 workspace では履歴が分離される。
- workspace 配下に新しい composer history file が作られない。
- user data dir 配下に workspace-scoped な composer history が保存される。
- Pod session log / Worker history / transcript には、recall 操作だけでは何も追加されない。
- focused test または明確な手動確認手順で、永続化・workspace 分離・既存 recall semantics を検証できる。
@@ -0,0 +1,7 @@
<!-- event: create author: tickets.sh at: 2026-06-01T02:11:04Z -->
## Created
Created by tickets.sh create.
---
@@ -0,0 +1,89 @@
---
id: 20260601-064953-plugin-distribution-package-format
slug: plugin-distribution-package-format
title: Plugin distribution package format and discovery
status: open
kind: feature
priority: P2
labels: [plugin, distribution, workspace, user]
created_at: 2026-06-01T06:49:53Z
updated_at: 2026-06-01T06:50:33Z
assignee: null
legacy_ticket: null
---
## Background
The plugin extension surface ticket (`plugin-extension-surface`) defines Plugins as a safe contribution model for Tools and Hooks, with MCP, declarative hooks, and WASM treated as runtime mechanisms. The next design question is how plugins are distributed, discovered, installed, and enabled across user and workspace scopes.
The desired initial direction is a single-file plugin package that can be placed in user or workspace plugin stores, for example:
- `~/.config/insomnia/plugins/<id>.insomnia-plugin`
- `./.insomnia/plugins/<id>.insomnia-plugin`
The package should be easy to copy, inspect, cache, and pin, while preserving Insomnia's scope, permission, history, prompt-context, and trust invariants. In particular, workspace plugins may come from a repository checkout and must not execute merely because an archive exists under `./.insomnia/plugins`.
## Requirements
- Define a first-class plugin package format.
- Use a single archive file with an Insomnia-specific extension such as `.insomnia-plugin`.
- Require a root plugin manifest file such as `plugin.toml`.
- Support packaged assets such as `module.wasm`, JSON schemas, README, and license files.
- Specify archive safety rules, including path traversal rejection, bounded extraction, and deterministic digest calculation.
- Define plugin stores and source/trust mapping.
- User plugin store: `~/.config/insomnia/plugins/`.
- Workspace/project plugin store: `./.insomnia/plugins/`.
- Map stores to the existing source vocabulary (`User`, `Project`/workspace, and future `Builtin`).
- Treat `user:<id>` and `project:<id>` as distinct plugin references; ambiguous unqualified IDs should fail closed.
- Separate discovery from enablement.
- Insomnia may discover plugin packages in configured stores.
- Discovered packages must not register Tools/Hooks, initialize WASM, or start MCP servers until explicitly enabled by manifest/profile configuration.
- Enablement must resolve package identity, version/API compatibility, source, digest, requested capabilities, and host-granted capabilities.
- Define package manifest semantics.
- Include fields for plugin id, version, plugin API version, runtime kind, source/provenance, metadata, contributed tools/hooks, configuration schema, and requested capabilities.
- Capability declarations are requests, not grants; effective grants remain controlled by manifest/profile policy, scope, permissions, web/network policy, secret references, and runtime-specific allowlists.
- Define runtime-specific packaging expectations.
- Declarative hooks can be packaged as config-only assets without arbitrary code execution.
- WASM plugins should package a module plus schemas/assets and run only with explicit host imports/capabilities.
- MCP should remain modeled as a backend/bridge; packaging an MCP server or process command requires explicit process/capability design and must not auto-start from workspace packages.
- Define cache/pinning behavior.
- Extract or materialize packages into a digest-keyed cache before runtime initialization.
- Consider digest pins in manifest/profile enablement entries or a future lock file.
- Record resolved package digest/source/provenance in the resolved manifest/session metadata where appropriate.
- Define diagnostics.
- Report load/parse/compatibility/capability/runtime failures with plugin id, source, runtime kind, and phase.
- Diagnostics must not expose secret values, raw credentials, or unsafe command/environment details.
## Non-goals
- Implementing the full plugin runtime system in this ticket.
- Implementing a package registry or network installer.
- Auto-enabling plugins solely because they are present in a plugin directory.
- Defining UI/TUI rendering extension packaging.
- Allowing arbitrary host scripting languages as plugin packages.
- Starting workspace-provided MCP servers without explicit enablement and capability approval.
## Suggested phases
1. **Architecture note**
- Define `.insomnia-plugin` package structure, `plugin.toml` fields, source/trust model, discovery vs enablement, archive safety, cache/digest behavior, and runtime mappings.
2. **Manifest/profile config shape**
- Add or propose `[plugins]` enablement entries with source/id/version/digest selectors and capability grants.
3. **Package discovery prototype**
- Implement read-only discovery of user/workspace plugin packages and diagnostics without runtime initialization.
4. **Package validation and cache**
- Validate archive layout, parse `plugin.toml`, compute digest, and materialize into a digest-keyed cache.
5. **Registry integration**
- Connect validated packages to the plugin contribution registry from `plugin-extension-surface` follow-up work.
6. **Runtime-specific follow-ups**
- Split declarative hook packaging, WASM packaging, and MCP packaging/bridge behavior into separate tickets as needed.
## Acceptance criteria
- The repository has a documented plugin distribution/package proposal covering user and workspace plugin stores, single-file archive format, manifest fields, archive safety, cache/digest behavior, and discovery vs enablement.
- The proposal explicitly states that placing a package in `~/.config/insomnia/plugins/` or `./.insomnia/plugins/` is discovery only, not execution or registration.
- The design maps package sources to user/project/builtin trust categories and defines how ID collisions and ambiguous selectors are handled.
- The design explains how capability requests differ from host-granted capabilities and how existing scope/permission/secret/web policy remains authoritative.
- Runtime-specific notes cover declarative hooks, WASM packages, and MCP backend/bridge packaging constraints.
- Follow-up implementation tickets can be cut independently for manifest/profile enablement, package discovery, archive validation/cache, WASM packaging, and MCP packaging alignment.
- Any code changes in this ticket, if taken beyond design docs, are limited to safe internal boundaries and focused tests.
@@ -0,0 +1,22 @@
<!-- event: create author: tickets.sh at: 2026-06-01T06:49:53Z -->
## Created
Created by tickets.sh create.
---
<!-- event: decision author: hare at: 2026-06-01T06:50:33Z -->
## Decision
Distribution direction from user discussion:
- Initial plugin packages should be single-file archives placed in user or workspace plugin stores, such as `~/.config/insomnia/plugins/` and `./.insomnia/plugins/`.
- Package presence is discovery only. Tool/Hook registration, WASM initialization, or MCP process startup requires explicit manifest/profile enablement and capability grant resolution.
- The package should contain a root `plugin.toml` and optional runtime assets such as `module.wasm`, schemas, README, and license files.
- User and workspace stores map to source/trust categories. `user:<id>` and `project:<id>` are distinct; ambiguous selectors should fail closed.
- Workspace packages are repository-provided and must be treated conservatively, especially for executable runtimes and MCP/process-spawn behavior.
---
@@ -0,0 +1,364 @@
# Dependency/license audit report
Date: 2026-06-01
Scope: read-mostly dependency and license audit for the Yoi workspace at `/home/hare/Projects/yoi`, per `artifacts/delegation-intent.md`. I did not modify dependency manifests, source code, lockfiles, docs, or ticket files other than this report artifact. I did not read ignored secret-like file contents.
## Executive summary
No dependency-license incompatibility blocker was identified from the available local metadata. The main release-risk gap is process/packaging: there is no checked-in dependency license policy or generated third-party notices artifact, so a public binary/source release should add one before publication if notices are expected to ship with the release.
The clearest cleanup candidates are non-blocking: normalize `reqwest` TLS features so Yoi does not enable both native OpenSSL TLS and rustls-related TLS paths, align the direct `crossterm` version with `ratatui`'s backend dependency, and periodically review the HTML/YAML parsing stacks for weight/maintenance.
## Methodology and commands used
Evidence came from local manifests, lock/metadata commands, dependency trees, and source usage greps. Commands were read-only except for writing this report.
File reads:
- `artifacts/delegation-intent.md`
- root `Cargo.toml`, `Cargo.lock`, workspace crate `Cargo.toml` files under `crates/*/Cargo.toml`
- `LICENSE`
- `flake.nix`, `package.nix`, `devshell.nix`
Inventory and license commands:
```sh
cd /home/hare/Projects/yoi
cargo metadata --locked --format-version 1
cargo metadata --locked --format-version 1 | jq -r '...direct workspace dependency grouping...'
cargo metadata --locked --format-version 1 | jq -r '...license field grouping and concerning-license filters...'
cargo deny check licenses
cargo deny --locked --offline list -f tsv
cargo deny --locked --offline --all-features list -f tsv
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...license counts...'
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...non-standard/copyleft/notice-relevant license packages...'
cargo tree --locked -e features -i reqwest@0.13.2
cargo tree --locked -i openssl-sys@0.9.112 --all-features
cargo tree --locked -i native-tls@0.2.18 --all-features
cargo tree --locked -i rustls@0.23.37 --all-features
cargo tree --locked --duplicates --all-features
cargo tree --locked -e no-dev --prefix none | sort -u | wc -l
cargo tree --locked -e no-dev --duplicates
```
Source usage checks:
```sh
rg 'use reqwest|reqwest::|ClientBuilder|Client::builder' crates/**/*.rs
rg 'html5ever|markup5ever|RcDom|parse_document' crates/**/*.rs
rg 'serde_yaml|frontmatter|yaml|YAML' crates/**/*.rs
rg 'zstd|encode_all|decode_all' crates/**/*.rs
rg 'mlua::|Lua::|LuaSerdeExt|require\(' crates/**/*.rs
rg 'crossterm::|ratatui::' crates/**/*.rs
```
Fallbacks / tool notes:
- `python3` was unavailable in the audit environment, so JSON processing used `jq` and `awk`.
- `cargo deny check licenses` exited non-zero because no project license policy/config is present; I used `cargo deny ... list -f tsv` as a local metadata fallback rather than treating the policy failure itself as license evidence.
- No web lookup was used. License conclusions are therefore limited to local crates.io metadata / cargo-deny parsing and local manifests.
## Workspace/dependency shape
- Root workspace: 19 crates, workspace package license `MIT`.
- Project license file: `LICENSE` is MIT.
- `cargo metadata --locked --format-version 1` reported 486 package records in the resolved metadata set.
- `cargo tree --locked -e no-dev --prefix none | sort -u | wc -l` reported 404 unique non-dev tree lines.
- Direct external dependencies found across workspace manifests: 45 unique package names including dev/build-only dependencies.
## Direct Rust dependencies and rough purpose notes
### Runtime/build dependencies
| Dependency | Direct users | Rough purpose / usage note |
| --- | --- | --- |
| `arc-swap` | `manifest`, `pod` | Shared mutable configuration/state handles. |
| `async-trait` | `llm-worker`, `memory`, `pod`, `provider`, `tools` | Async trait object ergonomics for tool/provider abstractions. |
| `base64` | `provider` | Codex/OAuth or provider token/body encoding helpers. |
| `chrono` | `lint-common`, `memory`, `pod`, `provider`, `workflow` | Timestamps, serde timestamps, workflow/memory metadata. |
| `clap` | `pod` runtime; `llm-worker` dev | CLI parsing. |
| `crossterm` | `tui` | Terminal input/output/events. Direct version is `0.28`; `ratatui` pulls `0.29` transitively. |
| `eventsource-stream` | `llm-worker` | SSE stream parsing for LLM/provider responses. |
| `fs4` | `pod`, `pod-registry` | Cross-process file locking. |
| `futures` | `llm-worker`; dev in `pod`, `session-store` | Stream/future helpers. |
| `globset`, `ignore`, `grep-matcher`, `grep-regex`, `grep-searcher` | `tools` | Local Glob/Grep tool implementation, gitignore-aware search. |
| `html5ever`, `markup5ever_rcdom` | `tools` | WebFetch HTML parsing and Readability-style extraction (`tools/src/web.rs`). |
| `include_dir` | `pod` | Compile-time embedding of prompt/profile/runtime resources. |
| `libc` | `memory`, `pod`, `pod-registry` | Unix process/permission/runtime details. |
| `minijinja` | `pod` | Prompt/template rendering. |
| `mlua` | `manifest` | Lua profile evaluation with vendored Lua 5.4 and serde integration. |
| `proc-macro2`, `quote`, `syn` | `llm-worker-macros` | Procedural macro implementation. |
| `pulldown-cmark` | `tui` | Markdown rendering/parsing in terminal UI. |
| `ratatui`, `unicode-width` | `tui` | Terminal UI rendering and width calculations. |
| `reqwest` | `llm-worker`, `provider`, `tools` | HTTP client for LLM transport, OAuth refresh, WebSearch/WebFetch. |
| `schemars` | `memory`, `pod`, `tools`; `llm-worker` dev | JSON schema generation for tools/config/test surfaces. |
| `serde`, `serde_json`, `serde_ignored`, `toml` | many crates | Serialization and config/profile parsing. |
| `serde_yaml` | `memory`, `workflow` | YAML frontmatter parsing for memory/workflow/skill documents. |
| `sha2` | `memory`, `secrets`, `tools` | Hashing/audit/secret-integrity and web/cache utilities. |
| `tempfile` | `tools` runtime; many crates dev | Temporary files/directories for command/tool execution and tests. |
| `thiserror` | many crates | Typed error definitions. |
| `tokio`, `tokio-util` | many crates | Async runtime, process/socket/time/file operations, stream utilities. |
| `tracing` | many crates | Structured runtime logging. |
| `uuid` | `client`, `memory`, `pod`, `protocol`, `session-store`, `tui` | Session/run/Pod identifiers; v7 and serde features where needed. |
| `zstd` | `llm-worker` | Codex backend request compression; usage confirmed in `llm_client/transport.rs`. |
### Dev-only direct dependencies
| Dependency | Direct users | Rough purpose / usage note |
| --- | --- | --- |
| `dotenv` | `llm-worker`, `pod` dev | Local dev/test credential loading. Not a runtime dependency. |
| `filetime` | `tools` dev | Filesystem timestamp testing. |
| `serial_test` | `provider` dev | Serializes provider tests that mutate shared state. |
| `tracing-subscriber` | `llm-worker` dev | Test/example logging setup. |
| `trybuild` | `llm-worker` dev | Proc-macro compile-fail/compile-pass tests. |
| `wiremock` | `llm-worker`, `provider` dev | Mock HTTP server for provider/client tests. |
## Transitive/license summary
### Local project license
- Workspace package license: `MIT` in root `Cargo.toml`.
- Repository `LICENSE`: MIT.
- Nix package metadata: `meta.license = lib.licenses.mit`.
### Cargo metadata / cargo-deny findings
`cargo metadata` showed no packages with missing `license` metadata.
`cargo deny --locked --offline --all-features list -f tsv` produced the following license-column counts. Counts include packages that offer multiple license alternatives, so the total exceeds the number of packages.
| License column | Count |
| --- | ---: |
| MIT | 348 |
| Apache-2.0 | 254 |
| Unicode-3.0 | 19 |
| Unlicense | 10 |
| Apache-2.0 WITH LLVM-exception | 8 |
| ISC | 7 |
| BSD-3-Clause | 2 |
| LGPL-2.1-or-later | 2 |
| BSD-2-Clause | 1 |
| BSL-1.0 | 1 |
| CC0-1.0 | 1 |
| CDLA-Permissive-2.0 | 1 |
| MIT-0 | 1 |
| OpenSSL | 1 |
| Zlib | 1 |
### Unknown, missing, copyleft, non-standard, or notice-relevant licenses
No missing license metadata was observed locally.
Items to explicitly account for in a release license policy/notice flow:
- `r-efi@5.3.0` and `r-efi@6.0.0` have expression `MIT OR Apache-2.0 OR LGPL-2.1-or-later`. This is not a blocker if the project selects the permissive MIT/Apache alternative, but a policy tool should encode that choice so the LGPL alternative is not misread as an obligation.
- `aws-lc-sys@0.35.0` is reported with `ISC AND (Apache-2.0 OR ISC) AND OpenSSL`; `aws-lc-rs`, `rustls`, `hyper-rustls`, `rustls-native-certs`, `rustls-webpki`, and `untrusted` also show ISC-family entries. The OpenSSL marker is notice-relevant and should be included in third-party notices if that path remains enabled.
- `webpki-root-certs@1.0.5` is `CDLA-Permissive-2.0`; include in notices/policy.
- ICU4X-related crates (`icu_*`, `zerovec*`, `zerofrom*`, `yoke*`, `tinystr`, `litemap`, `writeable`, `potential_utf`, `unicode-ident`) carry `Unicode-3.0`; include in policy/notices.
- `rustix`, `linux-raw-sys`, `wasi`, `wasip2`, `wasip3`, `wit-bindgen` include `Apache-2.0 WITH LLVM-exception`; standard permissive but notice-relevant.
- `globset`, `ignore`, `grep-*`, `aho-corasick`, `memchr`, `same-file`, `walkdir`, `winapi-util` include `Unlicense OR MIT`; select MIT or otherwise account for Unlicense acceptance in policy.
- `encoding_rs` / `subtle` show BSD-3-Clause, `zerocopy` shows BSD-2-Clause alternative, `ryu` shows BSL-1.0 alternative, `foldhash` shows Zlib, and `dunce` shows CC0/MIT-0/Apache alternatives. These are not blockers but should be represented in generated notices.
A broader `cargo metadata` license-field scan also surfaced `terminfo@0.9.0` with `WTFPL`, but `cargo tree` did not show it in the active default/all-features tree for Yoi; reverse metadata edges point through optional `termwiz`/`ratatui-termwiz`. I would not treat this as a release blocker based on current tree evidence, but a future policy check should confirm inactive optional dependencies are excluded or explicitly allowed.
## Heavy/redundant/replaceable dependency candidates
### 1. `reqwest` TLS feature duplication — high confidence cleanup candidate
Evidence:
- `llm-worker` and `tools` declare `reqwest` with `default-features = false` plus `native-tls`.
- `provider` declares `reqwest = { version = "0.13", features = ["json", "native-tls"] }` without `default-features = false`.
- `cargo tree --locked -e features -i reqwest@0.13.2` showed `provider` enabling `reqwest feature "default"`, which then enables `default-tls` and rustls-related features, while `native-tls` is also enabled.
- Inverse trees showed both `openssl-sys -> native-tls -> hyper-tls -> reqwest` and `rustls -> hyper-rustls -> reqwest` in the graph.
Impact:
- Larger dependency graph and binary/build surface.
- Keeps Nix `openssl`/`pkg-config` system dependency necessary via native TLS.
- Adds license/notice surface from both native TLS/OpenSSL and rustls/aws-lc paths.
Recommendation:
- Open a follow-up to choose one TLS policy for Yoi HTTP clients. If native certificate store behavior is required, encode that intentionally. If rustls is sufficient, remove native OpenSSL TLS and revisit Nix `openssl`/`pkg-config` inputs. If native TLS is preferred, disable `reqwest` defaults consistently so rustls/default TLS paths are not accidentally enabled.
### 2. Duplicate `crossterm` versions — high confidence cleanup candidate
Evidence:
`cargo tree --locked --duplicates --all-features` shows:
```text
crossterm v0.28.1
└── tui v0.1.0
crossterm v0.29.0
└── ratatui-crossterm v0.1.0
└── ratatui v0.30.0
└── tui v0.1.0
```
Impact:
- Duplicate terminal backend stack and some duplicated transitive platform crates.
- Likely avoidable by aligning direct `crossterm` with `ratatui`'s backend dependency, if API changes are small.
Recommendation:
- Open a small cleanup ticket to update direct `crossterm` to the version used by `ratatui-crossterm`, run TUI/input tests, and remove the duplicate if compatible.
### 3. HTML extraction stack (`html5ever` + `markup5ever_rcdom`) — medium confidence review candidate
Evidence:
- Direct dependency in `tools`.
- Usage is localized to WebFetch HTML parsing/extraction in `crates/tools/src/web.rs` (`html5ever::parse_document`, `RcDom`).
- Duplicate tree evidence includes older `syn v1`, `phf_*`, `siphasher`, `markup5ever`, and related build-time transitive crates from this stack.
Impact:
- This is a relatively heavy parser stack for one subsystem, but it implements a real product requirement: robust local HTML extraction without sending raw HTML to the model.
Recommendation:
- Do not remove opportunistically. Open a follow-up only if WebFetch binary size/build time becomes a priority; compare with a maintained lighter parser/extractor while preserving safety behavior.
### 4. `serde_yaml` frontmatter parsing — medium confidence maintenance review candidate
Evidence:
- Direct users: `memory`, `workflow`.
- Usage is frontmatter/skill/workflow parsing and linting.
Impact:
- YAML is appropriate for frontmatter, but the Rust YAML ecosystem has maintenance caveats. This is not a license blocker from local metadata.
Recommendation:
- Non-blocking follow-up: decide whether to keep `serde_yaml`, switch to a maintained fork, or constrain frontmatter to a smaller parser-supported subset. This requires design judgment because it affects user-authored workflow/memory files.
### 5. Dev/test HTTP stack (`wiremock`, `serial_test`, `trybuild`) — low confidence cleanup candidate
Evidence:
- `wiremock` appears only in dev dependencies for `llm-worker` and `provider`.
- `serial_test` and `trybuild` are dev-only.
Impact:
- They increase test dependency graph, not runtime release surface.
Recommendation:
- No release action needed. Only revisit if CI time or test dependency policy becomes a problem.
### 6. `mlua` vendored Lua — low confidence replacement candidate / justified heavy dependency
Evidence:
- Direct user: `manifest`.
- Source usage is concentrated in Lua Profile evaluation (`manifest/src/profile.rs`) with controlled `require("yoi.*")` modules.
Impact:
- Vendored interpreter is non-trivial build surface, but it supports a core profile-authoring direction.
Recommendation:
- Keep. Do not create a replacement ticket unless the product direction away from Lua Profiles is explicitly changed.
## Nix/system dependency notes
### `flake.nix`
- Inputs: `nixpkgs` from `github:nixos/nixpkgs?ref=nixos-unstable`, `flake-utils` from `github:numtide/flake-utils`.
- Outputs expose `packages.default`, `packages.yoi`, `apps.default`, `apps.yoi`, and `checks.default = yoi`.
- No extra system libraries are introduced in `flake.nix`; it delegates to `package.nix`.
### `package.nix`
- Build dependencies:
- `nativeBuildInputs = [ pkg-config ]`
- `buildInputs = [ openssl ]` plus Darwin frameworks `CoreFoundation`, `Security`, `SystemConfiguration` on macOS.
- `openssl`/`pkg-config` are consistent with the current native TLS path through `reqwest`/`native-tls`/`openssl-sys`.
- `meta.license = lib.licenses.mit` matches workspace/repo license.
- `cargoHash` is pinned.
- `depsExtraArgs` rewrites cargo vendor fetching from crates.io API download URLs to `static.crates.io` due an upstream/nixpkgs fetcher issue; this is packaging infrastructure, not a license concern.
- Source filter excludes `.git`, `target`, `result`, `.yoi`, `.worktree`, `work-items`, and `docs/report` from package source closure. This reduces accidental release of local coordination/generated state.
### `devshell.nix`
- Dev packages: `nixfmt`, `deno`, `git`, `rustc`, `cargo`.
- Dev build inputs: `pkg-config`, `openssl`.
- These are development/build tools, not bundled runtime dependencies. `deno` is present in the dev shell but not in `package.nix` or Cargo runtime dependencies.
## Release blockers vs non-blocking follow-ups
### Release blockers
No dependency license was identified as incompatible with MIT publication from the local metadata.
Potential process blocker before public distribution: third-party notices/license policy are not currently materialized. Apache-2.0, BSD, Unicode, CDLA, OpenSSL-marker, LLVM-exception, and other permissive licenses are acceptable in principle but should be included in a generated notice/policy artifact for release hygiene.
### Non-blocking follow-ups
- Normalize `reqwest` TLS features and decide whether Yoi wants native TLS/OpenSSL or rustls. This may also simplify Nix system dependencies.
- Align direct `crossterm` with `ratatui`'s `crossterm` backend to remove duplicate versions.
- Add CI-enforced license/dependency policy (`cargo-deny` or equivalent) and generated third-party notices.
- Review HTML parser stack only if size/build time is a concern.
- Review `serde_yaml` maintenance posture for frontmatter parsing; this is design-sensitive, not an obvious cleanup.
## Version update scan
Additional commands run after the initial audit:
```sh
cargo outdated --workspace --root-deps-only --format json > /tmp/yoi-cargo-outdated-root.json
cargo outdated --workspace --format json > /tmp/yoi-cargo-outdated-all.json
cargo update --dry-run
```
`cargo outdated --workspace --root-deps-only` reported the following direct workspace dependency updates:
| Dependency | Current | Latest reported | Kind | Direct users | Note |
| --- | ---: | ---: | --- | --- | --- |
| `reqwest` | 0.13.2 | 0.13.4 | normal | `llm-worker`, `provider`, `tools` | Patch update; should be combined with the TLS feature normalization follow-up. |
| `clap` | 4.6.0 | 4.6.1 | normal/dev | `pod`, `llm-worker` dev | Patch update. |
| `minijinja` | 2.19.0 | 2.20.0 | normal | `pod` | Minor update. |
| `crossterm` | 0.28.1 | 0.29.0 | normal | `tui` | Also matches the earlier duplicate-version finding with `ratatui`'s backend. |
| `pulldown-cmark` | 0.13.3 | 0.13.4 | normal | `tui` | Patch update. |
| `html5ever` | 0.26.0 | 0.39.0 | normal | `tools` | Major stack update; treat as WebFetch parser migration work, not routine bump. |
| `markup5ever_rcdom` | 0.2.0 | 0.39.0+unofficial | normal | `tools` | Major/unofficial stack update; tied to `html5ever` review. |
| `filetime` | 0.2.27 | 0.2.29 | dev | `tools` dev | Dev/test-only patch update. |
`cargo update --dry-run` reported that 80 locked packages can move to latest compatible versions without editing manifests. Notable compatible lockfile updates include:
- HTTP/TLS stack: `reqwest 0.13.2 -> 0.13.4`, `hyper 1.9.0 -> 1.10.1`, `h2 0.4.13 -> 0.4.14`, `rustls 0.23.37 -> 0.23.40`, `rustls-native-certs 0.8.3 -> 0.8.4`, `rustls-platform-verifier 0.6.2 -> 0.7.0`, `openssl 0.10.76 -> 0.10.80`, `openssl-sys 0.9.112 -> 0.9.116`, `aws-lc-rs 1.15.2 -> 1.17.0`, `aws-lc-sys 0.35.0 -> 0.41.0`.
- Core/runtime stack: `tokio 1.52.1 -> 1.52.3`, `uuid 1.23.1 -> 1.23.2`, `serde_json 1.0.149 -> 1.0.150`, `memchr 2.8.0 -> 2.8.1`, `indexmap 2.13.1 -> 2.14.0`, `socket2 0.6.3 -> 0.6.4`.
- UI/parser/dev stack: `minijinja 2.19.0 -> 2.20.0`, `pulldown-cmark 0.13.3 -> 0.13.4`, `filetime 0.2.27 -> 0.2.29`, `serial_test 3.4.0 -> 3.5.0`.
- Platform/wasm/windows support crates: multiple `wasm-bindgen`, `web-sys`, `windows-*`, `wasip2`, and `zerocopy` patch/minor updates.
Interpretation:
- There is a low-risk follow-up to run a lockfile refresh (`cargo update`) and validate it, but it should be separate from dependency policy changes because it touches many transitive packages.
- Direct manifest bumps can be grouped by risk: small patch/minor bumps (`reqwest`, `clap`, `minijinja`, `pulldown-cmark`, `filetime`) vs behavior/API-sensitive stack bumps (`crossterm`, `html5ever`, `markup5ever_rcdom`).
- The `reqwest` bump should not be done blindly before deciding the TLS feature policy, because the current audit already found accidental native-tls/rustls feature duplication.
## Recommended follow-up tickets
1. **Add dependency license policy and third-party notice generation**
- Acceptance: checked-in `cargo-deny` or equivalent policy; generated/reproducible notices for release artifacts; explicit choices for dual/multi-license crates such as `r-efi`, `Unlicense OR MIT`, and rustls/aws-lc/OpenSSL-marked crates; CI command documented.
2. **Normalize HTTP TLS backend and Nix OpenSSL dependency**
- Acceptance: all direct `reqwest` users consistently disable or enable defaults according to one documented TLS policy; dependency tree no longer unintentionally contains both `native-tls`/OpenSSL and rustls/default TLS paths unless intentionally justified; `package.nix` `openssl`/`pkg-config` inputs are retained or removed according to evidence.
3. **Deduplicate TUI terminal backend dependencies**
- Acceptance: direct `crossterm` version is aligned with `ratatui-crossterm` or duplicate is otherwise justified; TUI/input behavior is validated.
4. **Evaluate frontmatter YAML parser maintenance**
- Acceptance: decide to keep `serde_yaml`, migrate to a maintained fork, or specify a smaller frontmatter subset; include migration/compatibility implications for `.yoi/workflow`, memory, and skill files.
5. **Optional WebFetch parser weight review**
- Acceptance: compare current `html5ever`/`RcDom` extractor with viable maintained alternatives; preserve bounded, safe, link-aware extraction behavior; only proceed if measurable binary/build-time benefit exists.
@@ -0,0 +1,34 @@
# Delegation intent: dependency/license audit
Intent:
- Audit Yoi's external dependencies and license posture before public MIT publication.
Requirements:
- Inventory Rust dependencies from `Cargo.lock` / `cargo metadata`, separating direct workspace dependencies from transitive dependencies where practical.
- Identify direct dependencies that look heavy, weakly justified, redundant, or replaceable with simpler local code or already-present dependencies.
- Check license metadata for direct and transitive Rust dependencies; flag unknown, missing, copyleft, non-standard, or notice-relevant licenses.
- Inspect Nix/system dependencies from `flake.nix`, `package.nix`, and `devshell.nix` at a high level.
- Produce a report at `work-items/open/20260601-123641-dependency-license-audit/artifacts/audit-report.md`.
Invariants:
- Do not modify dependency manifests, source code, lockfiles, docs, or work item files other than the audit report artifact.
- Do not read ignored secret-like file contents.
- Treat Cargo/Nix files and command output as current-state evidence; do not rely on resident memory for exact dependency/license facts.
- Distinguish release blockers from advisory cleanup opportunities.
Non-goals:
- Do not remove dependencies.
- Do not change licenses.
- Do not implement replacements.
- Do not perform a public-release history sanitation audit beyond dependency/license implications.
Escalate if:
- A dependency appears incompatible with MIT publication.
- License metadata is missing for a significant dependency and cannot be resolved from local registry metadata without web access.
- A dependency replacement would require design judgment rather than obvious cleanup.
- Commands require network access or mutation beyond the artifact report.
Validation/evidence:
- Record every command used for inventory/license evidence.
- Prefer read-only commands such as `cargo metadata --locked`, `cargo tree --locked`, `nix flake metadata`, and direct file reads.
- If a tool is unavailable, record the fallback method.
@@ -0,0 +1,41 @@
---
id: 20260601-123641-dependency-license-audit
slug: dependency-license-audit
title: Audit external dependencies and license posture
status: open
kind: task
priority: P2
labels: [audit, dependencies, license]
created_at: 2026-06-01T12:36:41Z
updated_at: 2026-06-01T13:08:45Z
assignee: null
legacy_ticket: null
---
## Background
Before public MIT release, Yoi needs a focused audit of external dependencies and their licenses. The goal is not to remove every dependency, but to identify heavy or weakly-justified dependencies, dependencies that are easy to replace with simpler code or existing transitive functionality, and any licensing or notice obligations that conflict with an MIT release posture.
This is an investigation/audit ticket. It should produce an actionable report rather than immediate dependency changes.
## Requirements
- Inventory Rust crate dependencies from `Cargo.lock` / workspace metadata, including direct vs transitive status where practical.
- Identify direct dependencies that appear heavy, barely used, redundant, or plausibly replaceable.
- Check license metadata for direct and transitive Rust dependencies and flag unknown, copyleft, non-standard, or notice-relevant licenses.
- Inspect Nix/runtime/system dependencies declared by `flake.nix`, `package.nix`, and `devshell.nix` at a high level.
- Distinguish release blockers from advisory cleanup opportunities.
- Do not read ignored secret-like file contents.
- Do not modify dependency manifests as part of this ticket unless explicitly approved later.
## Acceptance criteria
- An artifact report is written under this ticket with:
- dependency inventory methodology;
- direct dependency list with rough purpose/usage notes;
- heavy/redundant/replaceable dependency candidates;
- license compatibility findings for an MIT publication posture;
- Nix/system dependency notes;
- recommended follow-up tickets, if any.
- Report clearly marks any release blockers vs non-blocking cleanup.
- Validation/evidence commands used for the audit are recorded.
@@ -0,0 +1,421 @@
<!-- event: create author: tickets.sh at: 2026-06-01T12:36:41Z -->
## Created
Created by tickets.sh create.
---
<!-- event: plan author: hare at: 2026-06-01T12:37:30Z -->
## Plan
# Delegation intent: dependency/license audit
Intent:
- Audit Yoi's external dependencies and license posture before public MIT publication.
Requirements:
- Inventory Rust dependencies from `Cargo.lock` / `cargo metadata`, separating direct workspace dependencies from transitive dependencies where practical.
- Identify direct dependencies that look heavy, weakly justified, redundant, or replaceable with simpler local code or already-present dependencies.
- Check license metadata for direct and transitive Rust dependencies; flag unknown, missing, copyleft, non-standard, or notice-relevant licenses.
- Inspect Nix/system dependencies from `flake.nix`, `package.nix`, and `devshell.nix` at a high level.
- Produce a report at `work-items/open/20260601-123641-dependency-license-audit/artifacts/audit-report.md`.
Invariants:
- Do not modify dependency manifests, source code, lockfiles, docs, or work item files other than the audit report artifact.
- Do not read ignored secret-like file contents.
- Treat Cargo/Nix files and command output as current-state evidence; do not rely on resident memory for exact dependency/license facts.
- Distinguish release blockers from advisory cleanup opportunities.
Non-goals:
- Do not remove dependencies.
- Do not change licenses.
- Do not implement replacements.
- Do not perform a public-release history sanitation audit beyond dependency/license implications.
Escalate if:
- A dependency appears incompatible with MIT publication.
- License metadata is missing for a significant dependency and cannot be resolved from local registry metadata without web access.
- A dependency replacement would require design judgment rather than obvious cleanup.
- Commands require network access or mutation beyond the artifact report.
Validation/evidence:
- Record every command used for inventory/license evidence.
- Prefer read-only commands such as `cargo metadata --locked`, `cargo tree --locked`, `nix flake metadata`, and direct file reads.
- If a tool is unavailable, record the fallback method.
---
<!-- event: implementation_report author: hare at: 2026-06-01T13:08:45Z -->
## Implementation report
# Dependency/license audit report
Date: 2026-06-01
Scope: read-mostly dependency and license audit for the Yoi workspace at `/home/hare/Projects/yoi`, per `artifacts/delegation-intent.md`. I did not modify dependency manifests, source code, lockfiles, docs, or ticket files other than this report artifact. I did not read ignored secret-like file contents.
## Executive summary
No dependency-license incompatibility blocker was identified from the available local metadata. The main release-risk gap is process/packaging: there is no checked-in dependency license policy or generated third-party notices artifact, so a public binary/source release should add one before publication if notices are expected to ship with the release.
The clearest cleanup candidates are non-blocking: normalize `reqwest` TLS features so Yoi does not enable both native OpenSSL TLS and rustls-related TLS paths, align the direct `crossterm` version with `ratatui`'s backend dependency, and periodically review the HTML/YAML parsing stacks for weight/maintenance.
## Methodology and commands used
Evidence came from local manifests, lock/metadata commands, dependency trees, and source usage greps. Commands were read-only except for writing this report.
File reads:
- `artifacts/delegation-intent.md`
- root `Cargo.toml`, `Cargo.lock`, workspace crate `Cargo.toml` files under `crates/*/Cargo.toml`
- `LICENSE`
- `flake.nix`, `package.nix`, `devshell.nix`
Inventory and license commands:
```sh
cd /home/hare/Projects/yoi
cargo metadata --locked --format-version 1
cargo metadata --locked --format-version 1 | jq -r '...direct workspace dependency grouping...'
cargo metadata --locked --format-version 1 | jq -r '...license field grouping and concerning-license filters...'
cargo deny check licenses
cargo deny --locked --offline list -f tsv
cargo deny --locked --offline --all-features list -f tsv
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...license counts...'
cargo deny --locked --offline --all-features list -f tsv | awk -F '\t' '...non-standard/copyleft/notice-relevant license packages...'
cargo tree --locked -e features -i reqwest@0.13.2
cargo tree --locked -i openssl-sys@0.9.112 --all-features
cargo tree --locked -i native-tls@0.2.18 --all-features
cargo tree --locked -i rustls@0.23.37 --all-features
cargo tree --locked --duplicates --all-features
cargo tree --locked -e no-dev --prefix none | sort -u | wc -l
cargo tree --locked -e no-dev --duplicates
```
Source usage checks:
```sh
rg 'use reqwest|reqwest::|ClientBuilder|Client::builder' crates/**/*.rs
rg 'html5ever|markup5ever|RcDom|parse_document' crates/**/*.rs
rg 'serde_yaml|frontmatter|yaml|YAML' crates/**/*.rs
rg 'zstd|encode_all|decode_all' crates/**/*.rs
rg 'mlua::|Lua::|LuaSerdeExt|require\(' crates/**/*.rs
rg 'crossterm::|ratatui::' crates/**/*.rs
```
Fallbacks / tool notes:
- `python3` was unavailable in the audit environment, so JSON processing used `jq` and `awk`.
- `cargo deny check licenses` exited non-zero because no project license policy/config is present; I used `cargo deny ... list -f tsv` as a local metadata fallback rather than treating the policy failure itself as license evidence.
- No web lookup was used. License conclusions are therefore limited to local crates.io metadata / cargo-deny parsing and local manifests.
## Workspace/dependency shape
- Root workspace: 19 crates, workspace package license `MIT`.
- Project license file: `LICENSE` is MIT.
- `cargo metadata --locked --format-version 1` reported 486 package records in the resolved metadata set.
- `cargo tree --locked -e no-dev --prefix none | sort -u | wc -l` reported 404 unique non-dev tree lines.
- Direct external dependencies found across workspace manifests: 45 unique package names including dev/build-only dependencies.
## Direct Rust dependencies and rough purpose notes
### Runtime/build dependencies
| Dependency | Direct users | Rough purpose / usage note |
| --- | --- | --- |
| `arc-swap` | `manifest`, `pod` | Shared mutable configuration/state handles. |
| `async-trait` | `llm-worker`, `memory`, `pod`, `provider`, `tools` | Async trait object ergonomics for tool/provider abstractions. |
| `base64` | `provider` | Codex/OAuth or provider token/body encoding helpers. |
| `chrono` | `lint-common`, `memory`, `pod`, `provider`, `workflow` | Timestamps, serde timestamps, workflow/memory metadata. |
| `clap` | `pod` runtime; `llm-worker` dev | CLI parsing. |
| `crossterm` | `tui` | Terminal input/output/events. Direct version is `0.28`; `ratatui` pulls `0.29` transitively. |
| `eventsource-stream` | `llm-worker` | SSE stream parsing for LLM/provider responses. |
| `fs4` | `pod`, `pod-registry` | Cross-process file locking. |
| `futures` | `llm-worker`; dev in `pod`, `session-store` | Stream/future helpers. |
| `globset`, `ignore`, `grep-matcher`, `grep-regex`, `grep-searcher` | `tools` | Local Glob/Grep tool implementation, gitignore-aware search. |
| `html5ever`, `markup5ever_rcdom` | `tools` | WebFetch HTML parsing and Readability-style extraction (`tools/src/web.rs`). |
| `include_dir` | `pod` | Compile-time embedding of prompt/profile/runtime resources. |
| `libc` | `memory`, `pod`, `pod-registry` | Unix process/permission/runtime details. |
| `minijinja` | `pod` | Prompt/template rendering. |
| `mlua` | `manifest` | Lua profile evaluation with vendored Lua 5.4 and serde integration. |
| `proc-macro2`, `quote`, `syn` | `llm-worker-macros` | Procedural macro implementation. |
| `pulldown-cmark` | `tui` | Markdown rendering/parsing in terminal UI. |
| `ratatui`, `unicode-width` | `tui` | Terminal UI rendering and width calculations. |
| `reqwest` | `llm-worker`, `provider`, `tools` | HTTP client for LLM transport, OAuth refresh, WebSearch/WebFetch. |
| `schemars` | `memory`, `pod`, `tools`; `llm-worker` dev | JSON schema generation for tools/config/test surfaces. |
| `serde`, `serde_json`, `serde_ignored`, `toml` | many crates | Serialization and config/profile parsing. |
| `serde_yaml` | `memory`, `workflow` | YAML frontmatter parsing for memory/workflow/skill documents. |
| `sha2` | `memory`, `secrets`, `tools` | Hashing/audit/secret-integrity and web/cache utilities. |
| `tempfile` | `tools` runtime; many crates dev | Temporary files/directories for command/tool execution and tests. |
| `thiserror` | many crates | Typed error definitions. |
| `tokio`, `tokio-util` | many crates | Async runtime, process/socket/time/file operations, stream utilities. |
| `tracing` | many crates | Structured runtime logging. |
| `uuid` | `client`, `memory`, `pod`, `protocol`, `session-store`, `tui` | Session/run/Pod identifiers; v7 and serde features where needed. |
| `zstd` | `llm-worker` | Codex backend request compression; usage confirmed in `llm_client/transport.rs`. |
### Dev-only direct dependencies
| Dependency | Direct users | Rough purpose / usage note |
| --- | --- | --- |
| `dotenv` | `llm-worker`, `pod` dev | Local dev/test credential loading. Not a runtime dependency. |
| `filetime` | `tools` dev | Filesystem timestamp testing. |
| `serial_test` | `provider` dev | Serializes provider tests that mutate shared state. |
| `tracing-subscriber` | `llm-worker` dev | Test/example logging setup. |
| `trybuild` | `llm-worker` dev | Proc-macro compile-fail/compile-pass tests. |
| `wiremock` | `llm-worker`, `provider` dev | Mock HTTP server for provider/client tests. |
## Transitive/license summary
### Local project license
- Workspace package license: `MIT` in root `Cargo.toml`.
- Repository `LICENSE`: MIT.
- Nix package metadata: `meta.license = lib.licenses.mit`.
### Cargo metadata / cargo-deny findings
`cargo metadata` showed no packages with missing `license` metadata.
`cargo deny --locked --offline --all-features list -f tsv` produced the following license-column counts. Counts include packages that offer multiple license alternatives, so the total exceeds the number of packages.
| License column | Count |
| --- | ---: |
| MIT | 348 |
| Apache-2.0 | 254 |
| Unicode-3.0 | 19 |
| Unlicense | 10 |
| Apache-2.0 WITH LLVM-exception | 8 |
| ISC | 7 |
| BSD-3-Clause | 2 |
| LGPL-2.1-or-later | 2 |
| BSD-2-Clause | 1 |
| BSL-1.0 | 1 |
| CC0-1.0 | 1 |
| CDLA-Permissive-2.0 | 1 |
| MIT-0 | 1 |
| OpenSSL | 1 |
| Zlib | 1 |
### Unknown, missing, copyleft, non-standard, or notice-relevant licenses
No missing license metadata was observed locally.
Items to explicitly account for in a release license policy/notice flow:
- `r-efi@5.3.0` and `r-efi@6.0.0` have expression `MIT OR Apache-2.0 OR LGPL-2.1-or-later`. This is not a blocker if the project selects the permissive MIT/Apache alternative, but a policy tool should encode that choice so the LGPL alternative is not misread as an obligation.
- `aws-lc-sys@0.35.0` is reported with `ISC AND (Apache-2.0 OR ISC) AND OpenSSL`; `aws-lc-rs`, `rustls`, `hyper-rustls`, `rustls-native-certs`, `rustls-webpki`, and `untrusted` also show ISC-family entries. The OpenSSL marker is notice-relevant and should be included in third-party notices if that path remains enabled.
- `webpki-root-certs@1.0.5` is `CDLA-Permissive-2.0`; include in notices/policy.
- ICU4X-related crates (`icu_*`, `zerovec*`, `zerofrom*`, `yoke*`, `tinystr`, `litemap`, `writeable`, `potential_utf`, `unicode-ident`) carry `Unicode-3.0`; include in policy/notices.
- `rustix`, `linux-raw-sys`, `wasi`, `wasip2`, `wasip3`, `wit-bindgen` include `Apache-2.0 WITH LLVM-exception`; standard permissive but notice-relevant.
- `globset`, `ignore`, `grep-*`, `aho-corasick`, `memchr`, `same-file`, `walkdir`, `winapi-util` include `Unlicense OR MIT`; select MIT or otherwise account for Unlicense acceptance in policy.
- `encoding_rs` / `subtle` show BSD-3-Clause, `zerocopy` shows BSD-2-Clause alternative, `ryu` shows BSL-1.0 alternative, `foldhash` shows Zlib, and `dunce` shows CC0/MIT-0/Apache alternatives. These are not blockers but should be represented in generated notices.
A broader `cargo metadata` license-field scan also surfaced `terminfo@0.9.0` with `WTFPL`, but `cargo tree` did not show it in the active default/all-features tree for Yoi; reverse metadata edges point through optional `termwiz`/`ratatui-termwiz`. I would not treat this as a release blocker based on current tree evidence, but a future policy check should confirm inactive optional dependencies are excluded or explicitly allowed.
## Heavy/redundant/replaceable dependency candidates
### 1. `reqwest` TLS feature duplication — high confidence cleanup candidate
Evidence:
- `llm-worker` and `tools` declare `reqwest` with `default-features = false` plus `native-tls`.
- `provider` declares `reqwest = { version = "0.13", features = ["json", "native-tls"] }` without `default-features = false`.
- `cargo tree --locked -e features -i reqwest@0.13.2` showed `provider` enabling `reqwest feature "default"`, which then enables `default-tls` and rustls-related features, while `native-tls` is also enabled.
- Inverse trees showed both `openssl-sys -> native-tls -> hyper-tls -> reqwest` and `rustls -> hyper-rustls -> reqwest` in the graph.
Impact:
- Larger dependency graph and binary/build surface.
- Keeps Nix `openssl`/`pkg-config` system dependency necessary via native TLS.
- Adds license/notice surface from both native TLS/OpenSSL and rustls/aws-lc paths.
Recommendation:
- Open a follow-up to choose one TLS policy for Yoi HTTP clients. If native certificate store behavior is required, encode that intentionally. If rustls is sufficient, remove native OpenSSL TLS and revisit Nix `openssl`/`pkg-config` inputs. If native TLS is preferred, disable `reqwest` defaults consistently so rustls/default TLS paths are not accidentally enabled.
### 2. Duplicate `crossterm` versions — high confidence cleanup candidate
Evidence:
`cargo tree --locked --duplicates --all-features` shows:
```text
crossterm v0.28.1
└── tui v0.1.0
crossterm v0.29.0
└── ratatui-crossterm v0.1.0
└── ratatui v0.30.0
└── tui v0.1.0
```
Impact:
- Duplicate terminal backend stack and some duplicated transitive platform crates.
- Likely avoidable by aligning direct `crossterm` with `ratatui`'s backend dependency, if API changes are small.
Recommendation:
- Open a small cleanup ticket to update direct `crossterm` to the version used by `ratatui-crossterm`, run TUI/input tests, and remove the duplicate if compatible.
### 3. HTML extraction stack (`html5ever` + `markup5ever_rcdom`) — medium confidence review candidate
Evidence:
- Direct dependency in `tools`.
- Usage is localized to WebFetch HTML parsing/extraction in `crates/tools/src/web.rs` (`html5ever::parse_document`, `RcDom`).
- Duplicate tree evidence includes older `syn v1`, `phf_*`, `siphasher`, `markup5ever`, and related build-time transitive crates from this stack.
Impact:
- This is a relatively heavy parser stack for one subsystem, but it implements a real product requirement: robust local HTML extraction without sending raw HTML to the model.
Recommendation:
- Do not remove opportunistically. Open a follow-up only if WebFetch binary size/build time becomes a priority; compare with a maintained lighter parser/extractor while preserving safety behavior.
### 4. `serde_yaml` frontmatter parsing — medium confidence maintenance review candidate
Evidence:
- Direct users: `memory`, `workflow`.
- Usage is frontmatter/skill/workflow parsing and linting.
Impact:
- YAML is appropriate for frontmatter, but the Rust YAML ecosystem has maintenance caveats. This is not a license blocker from local metadata.
Recommendation:
- Non-blocking follow-up: decide whether to keep `serde_yaml`, switch to a maintained fork, or constrain frontmatter to a smaller parser-supported subset. This requires design judgment because it affects user-authored workflow/memory files.
### 5. Dev/test HTTP stack (`wiremock`, `serial_test`, `trybuild`) — low confidence cleanup candidate
Evidence:
- `wiremock` appears only in dev dependencies for `llm-worker` and `provider`.
- `serial_test` and `trybuild` are dev-only.
Impact:
- They increase test dependency graph, not runtime release surface.
Recommendation:
- No release action needed. Only revisit if CI time or test dependency policy becomes a problem.
### 6. `mlua` vendored Lua — low confidence replacement candidate / justified heavy dependency
Evidence:
- Direct user: `manifest`.
- Source usage is concentrated in Lua Profile evaluation (`manifest/src/profile.rs`) with controlled `require("yoi.*")` modules.
Impact:
- Vendored interpreter is non-trivial build surface, but it supports a core profile-authoring direction.
Recommendation:
- Keep. Do not create a replacement ticket unless the product direction away from Lua Profiles is explicitly changed.
## Nix/system dependency notes
### `flake.nix`
- Inputs: `nixpkgs` from `github:nixos/nixpkgs?ref=nixos-unstable`, `flake-utils` from `github:numtide/flake-utils`.
- Outputs expose `packages.default`, `packages.yoi`, `apps.default`, `apps.yoi`, and `checks.default = yoi`.
- No extra system libraries are introduced in `flake.nix`; it delegates to `package.nix`.
### `package.nix`
- Build dependencies:
- `nativeBuildInputs = [ pkg-config ]`
- `buildInputs = [ openssl ]` plus Darwin frameworks `CoreFoundation`, `Security`, `SystemConfiguration` on macOS.
- `openssl`/`pkg-config` are consistent with the current native TLS path through `reqwest`/`native-tls`/`openssl-sys`.
- `meta.license = lib.licenses.mit` matches workspace/repo license.
- `cargoHash` is pinned.
- `depsExtraArgs` rewrites cargo vendor fetching from crates.io API download URLs to `static.crates.io` due an upstream/nixpkgs fetcher issue; this is packaging infrastructure, not a license concern.
- Source filter excludes `.git`, `target`, `result`, `.yoi`, `.worktree`, `work-items`, and `docs/report` from package source closure. This reduces accidental release of local coordination/generated state.
### `devshell.nix`
- Dev packages: `nixfmt`, `deno`, `git`, `rustc`, `cargo`.
- Dev build inputs: `pkg-config`, `openssl`.
- These are development/build tools, not bundled runtime dependencies. `deno` is present in the dev shell but not in `package.nix` or Cargo runtime dependencies.
## Release blockers vs non-blocking follow-ups
### Release blockers
No dependency license was identified as incompatible with MIT publication from the local metadata.
Potential process blocker before public distribution: third-party notices/license policy are not currently materialized. Apache-2.0, BSD, Unicode, CDLA, OpenSSL-marker, LLVM-exception, and other permissive licenses are acceptable in principle but should be included in a generated notice/policy artifact for release hygiene.
### Non-blocking follow-ups
- Normalize `reqwest` TLS features and decide whether Yoi wants native TLS/OpenSSL or rustls. This may also simplify Nix system dependencies.
- Align direct `crossterm` with `ratatui`'s `crossterm` backend to remove duplicate versions.
- Add CI-enforced license/dependency policy (`cargo-deny` or equivalent) and generated third-party notices.
- Review HTML parser stack only if size/build time is a concern.
- Review `serde_yaml` maintenance posture for frontmatter parsing; this is design-sensitive, not an obvious cleanup.
## Version update scan
Additional commands run after the initial audit:
```sh
cargo outdated --workspace --root-deps-only --format json > /tmp/yoi-cargo-outdated-root.json
cargo outdated --workspace --format json > /tmp/yoi-cargo-outdated-all.json
cargo update --dry-run
```
`cargo outdated --workspace --root-deps-only` reported the following direct workspace dependency updates:
| Dependency | Current | Latest reported | Kind | Direct users | Note |
| --- | ---: | ---: | --- | --- | --- |
| `reqwest` | 0.13.2 | 0.13.4 | normal | `llm-worker`, `provider`, `tools` | Patch update; should be combined with the TLS feature normalization follow-up. |
| `clap` | 4.6.0 | 4.6.1 | normal/dev | `pod`, `llm-worker` dev | Patch update. |
| `minijinja` | 2.19.0 | 2.20.0 | normal | `pod` | Minor update. |
| `crossterm` | 0.28.1 | 0.29.0 | normal | `tui` | Also matches the earlier duplicate-version finding with `ratatui`'s backend. |
| `pulldown-cmark` | 0.13.3 | 0.13.4 | normal | `tui` | Patch update. |
| `html5ever` | 0.26.0 | 0.39.0 | normal | `tools` | Major stack update; treat as WebFetch parser migration work, not routine bump. |
| `markup5ever_rcdom` | 0.2.0 | 0.39.0+unofficial | normal | `tools` | Major/unofficial stack update; tied to `html5ever` review. |
| `filetime` | 0.2.27 | 0.2.29 | dev | `tools` dev | Dev/test-only patch update. |
`cargo update --dry-run` reported that 80 locked packages can move to latest compatible versions without editing manifests. Notable compatible lockfile updates include:
- HTTP/TLS stack: `reqwest 0.13.2 -> 0.13.4`, `hyper 1.9.0 -> 1.10.1`, `h2 0.4.13 -> 0.4.14`, `rustls 0.23.37 -> 0.23.40`, `rustls-native-certs 0.8.3 -> 0.8.4`, `rustls-platform-verifier 0.6.2 -> 0.7.0`, `openssl 0.10.76 -> 0.10.80`, `openssl-sys 0.9.112 -> 0.9.116`, `aws-lc-rs 1.15.2 -> 1.17.0`, `aws-lc-sys 0.35.0 -> 0.41.0`.
- Core/runtime stack: `tokio 1.52.1 -> 1.52.3`, `uuid 1.23.1 -> 1.23.2`, `serde_json 1.0.149 -> 1.0.150`, `memchr 2.8.0 -> 2.8.1`, `indexmap 2.13.1 -> 2.14.0`, `socket2 0.6.3 -> 0.6.4`.
- UI/parser/dev stack: `minijinja 2.19.0 -> 2.20.0`, `pulldown-cmark 0.13.3 -> 0.13.4`, `filetime 0.2.27 -> 0.2.29`, `serial_test 3.4.0 -> 3.5.0`.
- Platform/wasm/windows support crates: multiple `wasm-bindgen`, `web-sys`, `windows-*`, `wasip2`, and `zerocopy` patch/minor updates.
Interpretation:
- There is a low-risk follow-up to run a lockfile refresh (`cargo update`) and validate it, but it should be separate from dependency policy changes because it touches many transitive packages.
- Direct manifest bumps can be grouped by risk: small patch/minor bumps (`reqwest`, `clap`, `minijinja`, `pulldown-cmark`, `filetime`) vs behavior/API-sensitive stack bumps (`crossterm`, `html5ever`, `markup5ever_rcdom`).
- The `reqwest` bump should not be done blindly before deciding the TLS feature policy, because the current audit already found accidental native-tls/rustls feature duplication.
## Recommended follow-up tickets
1. **Add dependency license policy and third-party notice generation**
- Acceptance: checked-in `cargo-deny` or equivalent policy; generated/reproducible notices for release artifacts; explicit choices for dual/multi-license crates such as `r-efi`, `Unlicense OR MIT`, and rustls/aws-lc/OpenSSL-marked crates; CI command documented.
2. **Normalize HTTP TLS backend and Nix OpenSSL dependency**
- Acceptance: all direct `reqwest` users consistently disable or enable defaults according to one documented TLS policy; dependency tree no longer unintentionally contains both `native-tls`/OpenSSL and rustls/default TLS paths unless intentionally justified; `package.nix` `openssl`/`pkg-config` inputs are retained or removed according to evidence.
3. **Deduplicate TUI terminal backend dependencies**
- Acceptance: direct `crossterm` version is aligned with `ratatui-crossterm` or duplicate is otherwise justified; TUI/input behavior is validated.
4. **Evaluate frontmatter YAML parser maintenance**
- Acceptance: decide to keep `serde_yaml`, migrate to a maintained fork, or specify a smaller frontmatter subset; include migration/compatibility implications for `.yoi/workflow`, memory, and skill files.
5. **Optional WebFetch parser weight review**
- Acceptance: compare current `html5ever`/`RcDom` extractor with viable maintained alternatives; preserve bounded, safe, link-aware extraction behavior; only proceed if measurable binary/build-time benefit exists.
---
@@ -0,0 +1,126 @@
# Delegation intent: migrate Ticket storage to `.yoi/tickets`
## Classification
`implementation-ready` with repository-record migration risk.
The prerequisite `builtin-yoi-local-ticket-backend-config` is complete: Ticket backend config now has canonical provider `builtin:yoi_local` while preserving transitional `work-items` storage. This ticket performs the storage move.
## Intent
Move the active built-in Yoi local Ticket backend storage from repository-root `work-items/` to `.yoi/tickets/` and make that the default/configured root for the Rust Ticket backend.
Target active layout:
```text
.yoi/tickets/{open,pending,closed}/<id>/item.md
.yoi/tickets/{open,pending,closed}/<id>/thread.md
.yoi/tickets/{open,pending,closed}/<id>/artifacts/
```
Target config example:
```toml
[backend]
provider = "builtin:yoi_local"
root = ".yoi/tickets"
```
## Worktree / branch
- worktree: `/home/hare/Projects/yoi/.worktree/migrate-ticket-storage-to-yoi-tickets`
- branch: `work/migrate-ticket-storage-to-yoi-tickets`
This ticket is an explicit exception to the normal child-worktree `.yoi` exclusion pattern because the implementation must add/modify tracked files under `.yoi/`. Do not read or edit `.yoi/memory/`; it is ignored generated memory state and is not part of this migration.
## Requirements
- Move tracked Ticket records from `work-items/` to `.yoi/tickets/` using git-tracked file moves.
- Update `crates/ticket/src/config.rs` defaults so missing Ticket config resolves to `<workspace>/.yoi/tickets`.
- Preserve canonical provider behavior from the previous ticket:
- `provider = "builtin:yoi_local"` remains the canonical provider;
- unsupported providers still fail closed;
- transitional `kind = "local"` handling should not become the documented path.
- Add or update the project `.yoi/ticket.config.toml` if needed so this repository explicitly configures:
```toml
[backend]
provider = "builtin:yoi_local"
root = ".yoi/tickets"
```
- Update code/docs/tests that refer to `work-items/` as the active backend root.
- Update `tickets.sh` only as a temporary compatibility/maintainer shim if it remains present after this ticket:
- it should operate on `.yoi/tickets` by default after the migration;
- its help text must stop claiming `work-items/` is canonical;
- keep `WORK_ITEMS_DIR` override if useful for recovery/back-compat.
- Keep Ticket tools, `yoi ticket ...`, TUI role launcher, and Pod Ticket feature registration working against the new root.
- Ensure `target/debug/yoi ticket doctor` / built binary equivalent sees the migrated `.yoi/tickets` records.
- Ensure `./tickets.sh doctor` works during the transition if `tickets.sh` still exists in this ticket.
## Non-goals
- Removing `tickets.sh`; that is the next ticket.
- Rewriting historical thread text/artifacts merely because they mention `work-items/`.
- Migrating generated memory, workflows, Pod sessions, or non-Ticket state.
- Changing Ticket role profile mappings, workflows, role launcher semantics, or TUI UI.
- External Ticket provider support.
## Current code map
- `crates/ticket/src/config.rs`
- Default root currently remains `work-items`; change to `.yoi/tickets`.
- Tests should be updated for the new default and explicit config root.
- `crates/pod/src/feature/builtin/ticket.rs`
- Uses `TicketConfig::load_workspace(...)` and `config.backend.root`; verify diagnostics/root handling remain correct.
- `crates/yoi/src/ticket_cli.rs`
- Uses Ticket config/backend root for `yoi ticket ...`; verify CLI commands against migrated storage.
- `tickets.sh`
- Still hardcodes `WORK_ITEMS_DIR=${WORK_ITEMS_DIR:-work-items}` and help text; update for transition or document if intentionally unsupported.
- `docs/development/work-items.md`
- Update active user documentation to `.yoi/tickets` and `yoi ticket`; `tickets.sh` should remain maintainer/transition-only until removal.
- `AGENTS.md` / project instructions references may still say `work-items/` is authoritative. Update active instructions if in scope so future agents do not mutate the wrong path.
## Migration cautions
- Do not create two active mutable roots. After migration, `.yoi/tickets` is active; `work-items/` should not remain as a second live backend.
- Do not leave open tickets split between old and new roots.
- Do not mass-rewrite old ticket thread prose solely for path hygiene.
- Verify lock-file behavior does not leave `.ticket-backend.lock` in the old root.
- Since the migration moves the ticket records themselves, expect the current ticket directory to move from `work-items/open/...` to `.yoi/tickets/open/...` in the implementation commit.
## Validation
Run at least:
- `cargo test -p ticket config`
- `cargo test -p ticket`
- `cargo test -p pod ticket --lib`
- `cargo test -p yoi ticket`
- `cargo check --workspace --all-targets`
- `cargo fmt --check`
- `git diff --check`
- `target/debug/yoi ticket doctor` or built binary equivalent
- `./tickets.sh doctor` during transition if still present
Run `nix build .#yoi --no-link` if feasible.
Also manually check:
- no active Ticket records remain under `work-items/` unless intentionally retained as a compatibility notice/stub;
- `.yoi/tickets/open`, `.yoi/tickets/pending`, and `.yoi/tickets/closed` contain the migrated records;
- new `yoi ticket create` creates under `.yoi/tickets` in a scratch temp workspace or controlled test fixture, not `work-items`.
## Completion report
Report:
- worktree path / branch;
- commit hash;
- final storage root and config behavior;
- whether `.yoi/ticket.config.toml` was added/updated;
- what happened to `work-items/`;
- whether `tickets.sh` was updated and how;
- docs/tests updated;
- validation results;
- whether `remove-tickets-sh` can proceed.
@@ -0,0 +1,66 @@
---
id: 20260605-203006-migrate-ticket-storage-to-yoi-tickets
slug: migrate-ticket-storage-to-yoi-tickets
title: Migrate Ticket storage to .yoi/tickets
status: open
kind: task
priority: P1
labels: [ticket, migration, storage]
created_at: 2026-06-05T20:30:06Z
updated_at: 2026-06-05T21:43:54Z
assignee: null
legacy_ticket: null
---
## Background
The active Ticket storage should move from top-level `work-items/` to `.yoi/tickets/` so Yoi project orchestration state lives under `.yoi/` with workflows and Ticket config.
This is a project-record migration. Preserve all existing Ticket ids, thread history, artifacts, and resolutions.
## Requirements
- Move active storage:
```text
work-items/open/ -> .yoi/tickets/open/
work-items/pending/ -> .yoi/tickets/pending/
work-items/closed/ -> .yoi/tickets/closed/
```
- Use `git mv` or equivalent tracked moves so history remains inspectable.
- Update code defaults/tests/docs/workflows to refer to `.yoi/tickets` as active storage.
- Update `.yoi/ticket.config.toml` examples/defaults to `root = ".yoi/tickets"`.
- Update worktree workflow guidance if necessary: child worktrees still exclude `.yoi`; Ticket mutation remains main-workspace/orchestrator authority.
- Ensure `.yoi/tickets` is tracked project state, not ignored generated memory.
- Adjust `.gitignore` if needed so `.yoi/tickets`, `.yoi/workflow`, `.yoi/knowledge`, and `.yoi/ticket.config.toml` can be tracked while generated `.yoi/memory` remains ignored.
- Do not rewrite historical thread/artifact body references unless they are current docs/config paths. Historical mentions of `work-items/` may remain as history.
## Non-goals
- Removing `tickets.sh`; handled later.
- Changing Ticket ids/slugs/status semantics.
- External tracker migration.
- Scheduler/lease/queue automation.
## Acceptance criteria
- All active Ticket records are under `.yoi/tickets/`.
- Top-level `work-items/` no longer exists as active storage.
- `yoi ticket list/show/doctor` works against `.yoi/tickets/`.
- Ticket tools and TUI role actions use `.yoi/tickets/` through configured backend root.
- Documentation no longer tells users to use `work-items/` as active storage.
- `git status` shows intentional tracked moves, not delete/recreate loss of records.
- Validation passes:
- `yoi ticket doctor`
- transitional `./tickets.sh doctor` only if still present and intentionally updated;
- `cargo check --workspace --all-targets`;
- `cargo fmt --check`;
- `git diff --check`.
## Dependency
Prefer after:
- `yoi-ticket-cli-parity`
- `builtin-yoi-local-ticket-backend-config`
@@ -0,0 +1,41 @@
<!-- event: create author: tickets.sh at: 2026-06-05T20:30:06Z -->
## Created
Created by tickets.sh create.
---
<!-- event: plan author: hare at: 2026-06-05T21:28:37Z -->
## Plan
Preflight result: `implementation-ready` with repository-record migration risk.
The provider-config prerequisite is complete. This ticket now owns the active storage move from `work-items/` to `.yoi/tickets/` and should make `.yoi/tickets` the default/configured built-in Yoi local backend root.
Important boundaries:
- no generated memory migration;
- do not read or edit `.yoi/memory/`;
- do not remove `tickets.sh` in this ticket, but update it as a transitional maintainer shim if it remains present;
- do not mass-rewrite historical thread prose solely because it mentions `work-items/`.
Detailed delegation intent is recorded in `artifacts/delegation-intent.md`.
---
<!-- event: implementation_report author: hare at: 2026-06-05T21:43:54Z -->
## Implementation report
Implemented the local Ticket storage migration to `.yoi/tickets/`.
- Moved tracked `work-items/{open,pending,closed}` records to `.yoi/tickets/{open,pending,closed}`.
- Added `.yoi/ticket.config.toml` with `provider = "builtin:yoi_local"` and `root = ".yoi/tickets"`.
- Updated default config resolution, Pod feature fallback, CLI tests/help, docs, and the transitional `tickets.sh` shim.
- Left `work-items/README.md` as a non-active compatibility notice only.
- Validated with the requested cargo tests/checks, both doctors, scratch default-create check, and `nix build .#yoi --no-link`.
---
@@ -0,0 +1,53 @@
---
id: 20260605-203006-remove-tickets-sh
slug: remove-tickets-sh
title: Remove tickets.sh compatibility CLI
status: open
kind: task
priority: P1
labels: [ticket, cleanup, cli]
created_at: 2026-06-05T20:30:06Z
updated_at: 2026-06-05T20:30:06Z
assignee: null
legacy_ticket: null
---
## Background
After `yoi ticket ...` has CLI parity and active Ticket storage has moved to `.yoi/tickets/`, the old shell compatibility CLI should be removed.
Keeping `tickets.sh` would leave a second mutation path and force duplicate semantics for create/comment/review/status/close/doctor.
## Requirements
- Delete `tickets.sh`.
- Remove or update every active doc/workflow/test reference that tells users/agents to run `./tickets.sh`.
- Replace validation references with `yoi ticket doctor`.
- Remove shell-specific tests if any, or port them to `yoi ticket ...` / Rust backend tests.
- Ensure no production code shells out to `tickets.sh`.
- Ensure all Ticket mutation paths use `crates/ticket` backend APIs or the `yoi ticket` CLI.
- Preserve historical Ticket thread/artifact mentions if they are closed historical context; do not rewrite old records unnecessarily.
## Non-goals
- Moving storage; must already be complete.
- Adding new Ticket features.
- External tracker support.
- TUI changes beyond documentation/help if needed.
## Acceptance criteria
- `tickets.sh` no longer exists.
- Repository docs/workflows no longer present `tickets.sh` as an active command.
- Validation docs use `yoi ticket doctor`.
- `rg "tickets.sh"` returns only closed historical records or no active references.
- `yoi ticket doctor` passes.
- `cargo check --workspace --all-targets`, `cargo fmt --check`, `git diff --check`, and relevant tests pass.
## Dependencies
Requires:
- `yoi-ticket-cli-parity`
- `builtin-yoi-local-ticket-backend-config`
- `migrate-ticket-storage-to-yoi-tickets`
@@ -0,0 +1,7 @@
<!-- event: create author: tickets.sh at: 2026-06-05T20:30:06Z -->
## Created
Created by tickets.sh create.
---
@@ -0,0 +1,106 @@
---
id: 20260605-203006-yoi-local-ticket-backend-migration
slug: yoi-local-ticket-backend-migration
title: Yoi-local Ticket backend migration
status: open
kind: task
priority: P1
labels: [ticket, backend, migration, cli]
created_at: 2026-06-05T20:30:06Z
updated_at: 2026-06-05T20:32:09Z
assignee: null
legacy_ticket: null
---
## Background
Ticket development is now user-facing through TUI role actions, typed Ticket tools, Ticket workflows, and the Rust `ticket` crate. The old `tickets.sh` CLI and top-level `work-items/` storage are no longer the right long-term authority boundary.
The desired end state is:
- `yoi` binary owns Ticket operations.
- Ticket backend is configured as a built-in Yoi local backend.
- Ticket records live under `.yoi/tickets/`.
- `tickets.sh` is removed.
- `work-items/` is removed after migration.
This is an umbrella for the migration. Child tickets should land in order so the repository remains operable at each step.
## Target model
```toml
# .yoi/ticket.config.toml
[backend]
provider = "builtin:yoi_local"
root = ".yoi/tickets"
```
Storage:
```text
.yoi/tickets/{open,pending,closed}/<id>/
item.md
thread.md
artifacts/
resolution.md # closed Tickets only
```
User-facing operations:
```text
yoi ticket create
yoi ticket list
yoi ticket show
yoi ticket comment
yoi ticket review
yoi ticket status
yoi ticket close
yoi ticket doctor
```
## Child tickets
1. `yoi-ticket-cli-parity`
- Add `yoi ticket ...` CLI parity over the Rust Ticket backend.
- Keep existing storage initially.
2. `builtin-yoi-local-ticket-backend-config`
- Add `provider = "builtin:yoi_local"` backend config and default root `.yoi/tickets`.
- Preserve compatibility with existing storage during transition.
3. `migrate-ticket-storage-to-yoi-tickets`
- Move existing records from `work-items/` to `.yoi/tickets/`.
- Update docs/workflows/tests/defaults.
4. `remove-tickets-sh`
- Remove `tickets.sh` after `yoi ticket ...` and `.yoi/tickets` are authoritative.
## Requirements
- Do not leave two authoritative mutation paths.
- Do not shell out from product code to `tickets.sh`.
- Preserve existing Ticket history and artifacts.
- Preserve `git history + Ticket files` as the durable project record.
- Keep child worktrees excluding `.yoi`; orchestration state remains in the main workspace.
- Keep `.yoi/tickets`, `.yoi/workflow`, and `.yoi/ticket.config.toml` tracked project state.
- Do not mix this migration with scheduler/lease/TUI dashboard work.
## Acceptance criteria
- All child tickets are closed.
- `yoi ticket ...` is the documented direct CLI path.
- Ticket tools and TUI role actions use the configured built-in backend.
- Existing records are under `.yoi/tickets/`.
- `work-items/` no longer exists as active storage.
- `tickets.sh` no longer exists.
- Repository validation uses `yoi ticket doctor` instead of `./tickets.sh doctor`.
- Docs explain user-facing TUI/Ticket tool workflows first and local backend details only as implementation details.
## Non-goals
- External tracker integration.
- GitHub/Linear/Jira/MCP backend support.
- Scheduler/lease/queue automation.
- Stateful workflow engine.
- Changing Ticket content semantics beyond storage/config migration.
@@ -0,0 +1,54 @@
<!-- event: create author: tickets.sh at: 2026-06-05T20:30:06Z -->
## Created
Created by tickets.sh create.
---
<!-- event: decision author: hare at: 2026-06-05T20:32:09Z -->
## Decision
Decision: migrate Ticket authority to the `yoi` binary and Yoi's built-in local backend.
Target state:
- Direct CLI operations use `yoi ticket ...`.
- Backend config uses `provider = "builtin:yoi_local"`.
- Active Ticket storage lives under `.yoi/tickets/`.
- `tickets.sh` is removed.
- Top-level `work-items/` is removed as active storage.
Rationale:
- Normal users should use TUI role actions, Ticket tools, workflows, and `yoi ticket ...`, not a shell script.
- Keeping `tickets.sh` as a live mutation path duplicates Ticket semantics and undermines the Rust backend as authority.
- `.yoi/tickets/` aligns Ticket records with `.yoi/workflow` and `.yoi/ticket.config.toml` as tracked project orchestration state.
- `work-items/` is legacy storage naming after the project concept was renamed to Ticket.
Migration should land in child tickets so the repository remains operable at each step.
---
<!-- event: plan author: hare at: 2026-06-05T20:32:09Z -->
## Plan
Plan:
1. `yoi-ticket-cli-parity`
- Add `yoi ticket ...` operations over the Rust Ticket backend.
2. `builtin-yoi-local-ticket-backend-config`
- Add canonical `provider = "builtin:yoi_local"` backend config and defaults.
3. `migrate-ticket-storage-to-yoi-tickets`
- Move active Ticket records from `work-items/` to `.yoi/tickets/`.
4. `remove-tickets-sh`
- Delete the shell compatibility CLI and update active docs/workflows/validation to `yoi ticket doctor`.
---
@@ -0,0 +1,71 @@
---
id: 20260605-210703-workspace-orchestration-panel
slug: workspace-orchestration-panel
title: Workspace orchestration panel
status: open
kind: task
priority: P1
labels: [tui, ticket, orchestration, panel]
created_at: 2026-06-05T21:07:03Z
updated_at: 2026-06-05T21:09:19Z
assignee: null
legacy_ticket: null
---
## Background
The current TUI Ticket role commands are a command-driven MVP. They prove that TUI can launch fixed Ticket-role Pods through the client launcher, but they do not provide the desired workspace orchestration experience.
The desired panel is a workspace-scoped orchestration UI, closer to an improved `--multi` surface, but organized around Ticket/Intake/Orchestrator action state rather than raw Pod idle/working state.
## Goal
Build a workspace orchestration panel where the user can:
- chat with a Companion management interface;
- send new requests directly to Ticket Intake without putting them into the Companion/current Pod session;
- have a workspace Orchestrator restored/spawned in the background when the panel opens;
- see user-action-required Intake/Ticket states before generic Pod status;
- give a simple Go signal once an Intake-prepared Ticket is fixed and understood by the human.
## Target model
- Panel is workspace-scoped.
- Companion is the foreground management chat target.
- Orchestrator is a background coordinator Pod named from the workspace directory, e.g. `<dir-name>-orchestrator`.
- Intake is spawned per user request from the composer target.
- Intake receives Orchestrator handoff/notification target at launch.
- Panel close does not stop Orchestrator.
- The primary list is an action model, not a Pod status list.
## Child tickets
1. `workspace-orchestration-panel-design`
- Produce the detailed architecture/design artifact for the panel.
2. `workspace-panel-orchestrator-lifecycle`
- Restore/spawn workspace Orchestrator when the panel opens; leave it running on panel close.
3. `workspace-panel-composer-targets`
- Add composer targets for Companion vs Ticket Intake; Intake sends the composer body directly to a new Intake Pod.
4. `ticket-intake-orchestrator-handoff`
- Define and implement Intake -> Orchestrator handoff/notification contract.
5. `workspace-panel-action-model`
- Display user-action-required Intake/Ticket states ahead of raw Pod status and support Go/Defer/Edit-style decisions.
## Non-goals
- Generic scheduler/lease/queue automation.
- Arbitrary role registry.
- Replacing the single-Pod TUI.
- Stopping background Orchestrator on panel close.
- Bypassing Ticket Intake / Routing / Preflight / Review gates.
## Acceptance criteria
- Child tickets are created and sequenced.
- The design ticket fixes the panel responsibility boundary before implementation tickets proceed.
- Implementation child tickets use existing Ticket tools/config/launcher where possible.
- The command-driven `:ticket ...` path remains available as a low-level fallback, but the panel design does not depend on users typing commands for the main flow.
@@ -0,0 +1,34 @@
<!-- event: create author: yoi ticket at: 2026-06-05T21:07:03Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: plan author: hare at: 2026-06-05T21:09:19Z -->
## Plan
Plan: design the workspace orchestration panel before implementation.
The panel is not a `:ticket` command extension. It is a workspace-scoped orchestration surface with:
- Companion foreground management chat;
- Ticket Intake composer target that sends user input directly to Intake, not Companion history;
- background workspace Orchestrator restored/spawned as `<dir-name>-orchestrator`;
- Intake -> Orchestrator handoff;
- an action model prioritizing human-required Ticket/Intake decisions over raw Pod idle state.
Implementation is split into child tickets:
1. `workspace-orchestration-panel-design`
2. `workspace-panel-orchestrator-lifecycle`
3. `workspace-panel-composer-targets`
4. `ticket-intake-orchestrator-handoff`
5. `workspace-panel-action-model`
Existing `:ticket ...` commands remain as low-level fallback, not the main UX.
---
@@ -0,0 +1,46 @@
---
id: 20260605-210704-ticket-intake-orchestrator-handoff
slug: ticket-intake-orchestrator-handoff
title: Ticket intake to orchestrator handoff
status: open
kind: task
priority: P1
labels: [ticket, intake, orchestrator, handoff]
created_at: 2026-06-05T21:07:04Z
updated_at: 2026-06-05T21:07:04Z
assignee: null
legacy_ticket: null
---
## Background
Intake can create or refine Tickets, but the next Orchestrator routing step is currently manual. The workspace panel needs a safe handoff from Intake to the workspace Orchestrator.
## Requirements
- Define a machine-readable handoff contract from Intake to Orchestrator.
- Handoff should include:
- created/updated Ticket id or slug;
- readiness;
- needs_preflight;
- risk flags when available;
- whether user Go/approval is required;
- summary of the Intake result.
- Intake launch should receive the Orchestrator notification target in its initial input or durable metadata, not hidden context.
- Orchestrator should be notified through an existing durable/observable channel where possible.
- Handoff should not automatically start implementation.
- The panel should be able to show a Ticket-ready-for-Go or routing-needed state after Intake.
## Non-goals
- Full scheduler/queue/lease.
- Automatic coder/reviewer spawn.
- Generic notification bus redesign.
- External tracker integration.
## Acceptance criteria
- Intake can hand off a newly created/refined Ticket to the workspace Orchestrator without the user manually retyping the Ticket id.
- Handoff is visible/auditable in history or Ticket records.
- Orchestrator routing is triggered or queued only within the agreed Go/authorization boundary.
- Tests or fixtures cover handoff payload parsing/formatting where practical.
@@ -0,0 +1,7 @@
<!-- event: create author: yoi ticket at: 2026-06-05T21:07:04Z -->
## Created
Created by LocalTicketBackend create.
---
@@ -0,0 +1,118 @@
# Workspace orchestration panel UI design draft
## Position
The workspace panel should be a successor to the current `--multi` surface, not a two-pane composition of `--multi` plus the normal single-Pod UI. The default unit should be Ticket/action state; Pods are execution/detail resources that the user can attach to on demand.
## UI shape
```text
Workspace Orchestration Panel
├─ header: workspace, orchestrator state, companion state
├─ primary list: user-action-required Tickets / Intakes / Reviews
├─ detail pane: selected Ticket plan, current phase, related Pods, latest reports
├─ optional timeline/plan view: umbrella/child dependency lanes and phase progress
└─ composer: explicit target selector
```
The main list must not be a raw Pod list. It should sort by user decision priority:
1. Intake needs clarification or approval.
2. Ticket is ready for human Go.
3. Review or close decision is required.
4. Ticket is blocked by an explicit human/project decision.
5. Active implementation/review work.
6. Informational Pod status and restorable historical sessions.
## Relationship to `--multi` and normal TUI
Reuse the useful `--multi` behaviors:
- discover visible live/restorable Pods;
- show live/stopped/working state;
- choose a send/attach target;
- attach to a Pod/session and return to the workspace panel;
- keep background Pods alive when the panel exits.
Do not embed the normal single-Pod UI as a permanent second pane. The panel should drill into a selected Pod/session only when the user explicitly attaches. This avoids maintaining two simultaneous chat surfaces and keeps normal Pod history ownership unchanged.
## Composer target model
The composer has an explicit target selector:
- `Companion`: management chat for this workspace panel session.
- `New Intake`: send the composer body as the initial user input of a new Ticket Intake Pod.
- `Selected Ticket`: record a comment/decision/Go/review-style action against the selected Ticket, usually via Ticket tools or an Orchestrator notification.
- `Selected Pod`: advanced/direct send or attach path, not the main workflow.
Dynamic user text must be committed to the destination authority:
- Companion text goes to Companion history.
- New Intake text goes to the Intake Pod initial user input/history.
- Ticket decisions/comments go to Ticket records and/or the Pod that receives the actual message.
- Nothing should be injected only into hidden context.
## Ticket-centric detail model
Each visible Ticket row should be backed by a plain data entry, not by direct render-time Pod inspection:
```text
TicketPanelEntry
- id / slug / title / status
- current phase: intake | preflight | implementing | reviewing | close_ready | blocked | closed
- next user action: clarify | go | review | close | wait | none
- parent/child relationship
- related role Pods and their live/restorable state
- latest plan / implementation report / review summary excerpts
- diagnostics and blocked reason
```
This model should be assembled from the Ticket backend, Ticket thread entries, known role launches, visible Pod metadata, and Orchestrator/Intake handoff records. The UI should consume this plain model so later views can be added without coupling rendering to Pod internals.
## Timeline / Gantt-like view
A useful first version is a phase/dependency timeline rather than a date-based scheduler:
```text
yoi-local-ticket-backend-migration
├─ ✓ yoi-ticket-cli-parity
├─ ▶ builtin-yoi-local-ticket-backend-config
├─ ○ migrate-ticket-storage-to-yoi-tickets
└─ ○ remove-tickets-sh
workspace-orchestration-panel
├─ ○ workspace-orchestration-panel-design
├─ ○ workspace-panel-orchestrator-lifecycle
├─ ○ workspace-panel-composer-targets
├─ ○ ticket-intake-orchestrator-handoff
└─ ○ workspace-panel-action-model
```
Per-ticket phase lanes can show progress without pretending the system has reliable time estimates:
```text
Ticket Intake Preflight Implement Review Close
backend cfg ✓ ✓ ▶ ○ ○
storage move ✓ ○ ○ ○ ○
panel design ▶ ○ ○ ○ ○
```
This captures what currently matters most: dependency order, gate state, responsible Pods, and human decision points. Duration/ETA scheduling can be added later if the backend starts recording enough authoritative timing data.
## Orchestrator lifecycle
When the workspace panel opens, restore or spawn a workspace Orchestrator named from the workspace directory, e.g. `<dir-name>-orchestrator`. The panel should display its state but should not own its lifetime; closing the panel leaves the Orchestrator running.
Intake Pods receive the Orchestrator handoff/notification target at launch so they can report clarified Ticket readiness without automatically starting implementation. The Orchestrator may prepare routing/preflight, but human Go remains an explicit action.
## Initial implementation boundary
Implement in this order:
1. Build the plain `WorkspacePanelModel` / `TicketPanelEntry` / `UserActionEntry` model.
2. Make the existing multi-Pod panel consume enough of that model to show action-prioritized Ticket rows.
3. Add composer target selection for Companion vs New Intake.
4. Add attach/drill-down and return behavior for related Pods.
5. Add the phase/dependency timeline view.
The existing `:ticket ...` commands remain as low-level fallback and debugging affordances, not the primary UX.
@@ -0,0 +1,56 @@
---
id: 20260605-210704-workspace-orchestration-panel-design
slug: workspace-orchestration-panel-design
title: Workspace orchestration panel design
status: open
kind: task
priority: P1
labels: [tui, design, orchestration, panel]
created_at: 2026-06-05T21:07:04Z
updated_at: 2026-06-05T21:22:49Z
assignee: null
legacy_ticket: null
---
## Background
The next TUI should not be another small `:` command extension. The desired feature is a workspace-scoped orchestration panel with explicit Companion/Intake composer targets, background Orchestrator lifecycle, Intake handoff, and a user-action-oriented model.
## Requirements
Produce a design artifact that fixes:
- panel entrypoint and relation to current `--multi`;
- Companion lifecycle and identity;
- workspace Orchestrator lifecycle and naming rule (`<dir-name>-orchestrator`);
- composer target model:
- Companion;
- Ticket Intake;
- how Intake launch avoids writing the user request into the Companion/current Pod history;
- how Intake receives Orchestrator notification/handoff target;
- action model priorities:
- user response required;
- Intake draft ready;
- Ticket ready for Go;
- blocked/action-required;
- review/close/preflight decisions;
- active background work;
- informational Pod status;
- Go semantics for a fixed Ticket;
- relationship to existing `:ticket ...` command fallback;
- failure/diagnostic behavior;
- implementation sequence.
## Non-goals
- Code implementation.
- Generic scheduler/lease/queue.
- Automatic implementation without Go/authorization.
- Replacing Ticket workflows.
## Acceptance criteria
- A design artifact exists under this Ticket's `artifacts/` directory.
- The artifact defines the state/action model and responsibility boundaries clearly enough to implement child tickets.
- The artifact explicitly preserves history/context rules: dynamic messages are committed to the destination Pod history, not injected into hidden context.
- Reviewer or parent approves the design before implementation child tickets proceed.
@@ -0,0 +1,24 @@
<!-- event: create author: yoi ticket at: 2026-06-05T21:07:04Z -->
## Created
Created by LocalTicketBackend create.
---
<!-- event: plan author: hare at: 2026-06-05T21:22:49Z -->
## Plan
Recorded an initial UI design draft from the panel discussion in `artifacts/workspace-panel-ui-design.md`.
Key direction:
- make the workspace panel a successor to `--multi`, not a permanent split with the normal single-Pod UI;
- use Ticket/action state as the default unit and attach to Pod/session details only on demand;
- provide an explicit composer target selector for Companion / New Intake / Selected Ticket / advanced Selected Pod;
- add a Gantt-like phase/dependency timeline based on Ticket gates and umbrella/child ordering rather than date estimates.
This is a design draft for review/iteration, not implementation approval for the child panel tickets yet.
---
@@ -0,0 +1,49 @@
---
id: 20260605-210704-workspace-panel-action-model
slug: workspace-panel-action-model
title: Workspace panel action model
status: open
kind: task
priority: P1
labels: [tui, ticket, orchestration, panel]
created_at: 2026-06-05T21:07:04Z
updated_at: 2026-06-05T21:07:04Z
assignee: null
legacy_ticket: null
---
## Background
The workspace panel should not be a plain Pod idle/working list. It should prioritize the items where the user needs to decide, respond, approve, or inspect evidence.
## Requirements
- Define and implement a workspace action model that can rank/display:
- Intake needs user reply;
- Intake draft ready;
- Ticket ready for Go;
- requirements sync needed;
- preflight needed;
- spike needed/running;
- implementation running;
- review needed;
- blocked/action-required;
- close-ready;
- background informational Pod status.
- Prefer Ticket/routing/intake state over raw Pod idle state.
- Provide Go/Defer/Edit-style actions where supported by preceding tickets.
- Preserve explicit human authorization boundaries.
- Do not treat Pod completion notifications as authority; verify Ticket/Pod/output state.
## Non-goals
- Scheduler/lease/queue automation.
- Full implementation of all role actions if earlier handoff/lifecycle tickets are not landed.
- Generic issue tracker UI.
## Acceptance criteria
- Panel displays user-action-required items above passive background Pods.
- Ticket-ready Go action is easy to trigger but does not bypass Orchestrator routing/preflight gates.
- Action model is testable as plain data independent of terminal rendering.
- Existing multi-Pod status data can still be shown as lower-priority background information.
@@ -0,0 +1,7 @@
<!-- event: create author: yoi ticket at: 2026-06-05T21:07:04Z -->
## Created
Created by LocalTicketBackend create.
---
@@ -0,0 +1,46 @@
---
id: 20260605-210704-workspace-panel-composer-targets
slug: workspace-panel-composer-targets
title: Workspace panel composer targets
status: open
kind: task
priority: P1
labels: [tui, composer, intake, panel]
created_at: 2026-06-05T21:07:04Z
updated_at: 2026-06-05T21:07:04Z
assignee: null
legacy_ticket: null
---
## Background
The workspace panel composer must let users choose whether a message goes to the Companion management chat or directly to Ticket Intake.
The key UX requirement is that a request sent to Intake must not be appended to the Companion/current Pod session history.
## Requirements
- Add a composer target model for the workspace panel:
- Companion;
- Ticket Intake.
- Companion target sends normal messages to the Companion Pod/session.
- Intake target launches a new Intake role Pod and sends the composer body as its first `Method::Run` input.
- Intake target must not send the body to Companion/current Pod history.
- Empty Intake messages are rejected.
- The UI clearly shows the active composer target.
- User can switch/cancel target without losing typed text where practical.
- Use Ticket role launcher rather than constructing spawn/profile/workflow prompt content inside UI.
## Non-goals
- Generic arbitrary target routing.
- Scheduler/queue.
- Intake -> Orchestrator handoff implementation.
- Action queue display.
## Acceptance criteria
- User can choose Companion or Intake before pressing Enter.
- Intake path creates a new Intake role launch with the typed body as dynamic run input.
- Existing Companion history does not receive the Intake body.
- Tests cover target switching and Enter behavior for both targets where practical.
@@ -0,0 +1,7 @@
<!-- event: create author: yoi ticket at: 2026-06-05T21:07:04Z -->
## Created
Created by LocalTicketBackend create.
---
@@ -0,0 +1,43 @@
---
id: 20260605-210704-workspace-panel-orchestrator-lifecycle
slug: workspace-panel-orchestrator-lifecycle
title: Workspace panel orchestrator lifecycle
status: open
kind: task
priority: P1
labels: [tui, pod, orchestrator, panel]
created_at: 2026-06-05T21:07:04Z
updated_at: 2026-06-05T21:07:04Z
assignee: null
legacy_ticket: null
---
## Background
The workspace orchestration panel needs a background Orchestrator Pod that is restored or spawned when the panel opens and remains alive after the panel closes.
## Requirements
- Derive Orchestrator Pod name from workspace directory, e.g. `<dir-name>-orchestrator`.
- On panel open:
- restore if restorable;
- attach/observe if already live;
- spawn if missing and permitted.
- Use `.yoi/ticket.config.toml` role profile for `orchestrator`.
- Use the Ticket role launcher where practical.
- Panel close must not stop the Orchestrator.
- Surface lifecycle diagnostics in the panel.
- Do not make Orchestrator the foreground composer target by default; Companion remains foreground management chat.
## Non-goals
- Full panel UI layout.
- Intake handoff contract.
- Scheduler/lease/queue.
- Automatic coder/reviewer spawning.
## Acceptance criteria
- Workspace panel startup can ensure an Orchestrator Pod exists or report why it cannot.
- Orchestrator lifecycle uses existing Pod restore/spawn semantics and does not duplicate registry logic.
- Tests cover name derivation and restore/spawn decision logic where practical.
@@ -0,0 +1,7 @@
<!-- event: create author: yoi ticket at: 2026-06-05T21:07:04Z -->
## Created
Created by LocalTicketBackend create.
---