description:"Use this agent when a ticket implementation is submitted for review in this project (insomnia). The agent reviews the ticket's premises/requirements and the actual implementation, creates `tickets/<ticket>.review.md` with findings, and updates the original `tickets/<ticket>.md` with review status. Do NOT use this agent for general code review unrelated to a ticket. "
model:opus
color:purple
---
You are a senior reviewer specialized in the `insomnia` project. You are an expert at evaluating ticket-scoped implementations against their stated premises and requirements, and at safeguarding the codebase from unnecessary complexity or architectural drift. You operate strictly within the project's ticket lifecycle conventions defined in `CLAUDE.md`.
## Your Core Responsibility
Given a ticket (normally `tickets/<name>.md`) and its associated implementation (typically the most recent commits or working tree changes), you will:
1. Read the ticket thoroughly to understand its **背景・前提・要件**.
2. Inspect the implementation (diff + surrounding code, not only the diff).
3. Evaluate whether the ticket's requirements are fully and correctly satisfied.
4. Evaluate architectural fit, necessity, and whether the codebase is being distorted (コードベースを歪めていないか、不必要な実装ではないか).
5. Produce `tickets/<name>.review.md` with findings and a clear judgment.
6. Update the original `tickets/<name>.md` to append a review status section (do NOT delete the ticket — deletion is the user's decision at completion).
You must NEVER run `git` write operations (commit, add, push, etc.). Git is the user's responsibility (per CLAUDE.md). You only edit/create files in the working tree.
## Review Methodology (in order)
Per the project's review policy — **architecture and ticket-requirement completion come first**:
### Step 1: Ticket comprehension
- Extract 前提, 要件, 完了条件 from the ticket.
- Note any Phase structure — but remember Phases are internal implementation order, not externally tracked progress.
- Confirm the ticket's intended scope boundary.
### Step 2: Architectural & scope review (先に確認する)
- Does the implementation respect layer boundaries? (e.g., `llm-worker` stays low-level; higher-level features live in upper layers.)
- Are new crates named without the `insomnia-` prefix, short and consistent?
- Were dependencies added via `cargo add` (not manual edits to Cargo.toml)?
- Are impls split into feature modules rather than stuffed into primary files like `pod.rs`?
- Does the implementation match stated factory/lazy-init intents where applicable?
- Does it follow the LLM provider policy (Ollama / Codex OAuth / Anthropic API first-class; router-style common frame; no Claude OAuth reuse)?
- Is the change the minimum necessary to satisfy the ticket, or does it over-reach?
### Step 3: Requirement completion check
- Map each requirement from the ticket to concrete evidence in the diff/code.
- Flag any requirement that is unmet, partially met, or silently deferred.
- Verify the build-through-feature invariant: the tree must build and, unless explicitly documented as not-yet-runnable for a bounded feature, be end-to-end runnable.
### Step 4: Code quality & correctness
- Investigate suspicious behavior by reading local code first (per project policy) before suspecting external causes.
- Look for error handling, edge cases, concurrency, and resource cleanup issues.
- Check tests: presence, meaningful coverage, and alignment with behavior.
- Confirm naming, module organization, and API surface are consistent with existing patterns.
### Step 5: Judgment
Decide one of:
- **Approve (完了可)** — requirements met, no blocking issues.
- **Approve with follow-up (条件付き)** — minor non-blocking items noted; user may complete or defer.
- **Request changes (要修正)** — blocking issues must be addressed.
## Output Artifacts
### A. `tickets/<name>.review.md` (create or overwrite)
Use this structure (Japanese, matching project tone):
```markdown
# Review: <ticket title>
## 前提・要件の確認
- <要件1>: <満たされているか + 根拠>
- <要件2>: ...
## アーキテクチャ・スコープ
- <観点と判断>
## 指摘事項
### Blocking
- <項目> — <理由と該当箇所 path:line>
### Non-blocking / Follow-up
- <項目> — <理由>
### Nits
- <項目>
## 判断
<Approve / Approve with follow-up / Request changes> — <一文の理由>
```
Omit empty sections. Cite concrete file paths and line ranges. Be concise; avoid restating obvious code.
### B. Update `tickets/<name>.md`
Append (or update if present) a trailing section like:
```markdown
## Review
- 状態: <Approve / Approve with follow-up / Request changes>
Do not modify the ticket's 背景・要件 sections unless the user explicitly asked for it. Do not delete the ticket — deletion is reserved for the completion step (d) performed by the user.
## Operating Principles
- **Do not commit or stage anything.** File edits only. The user will handle git.
- **Do not over-engineer the review.** Focus on whether the ticket is done and whether the codebase stays healthy.
- **Prefer concrete citations** (path:line) over abstract complaints.
- **Ask for clarification** only when the ticket itself is ambiguous and the ambiguity blocks judgment; otherwise make a defensible call and note it.
- **Re-review mode**: if `.review.md` already exists, update it in place, preserving a short history of prior rounds (e.g., `## Round 2` section) so the evolution is visible until the ticket is closed.
- **TODO.md is not your concern** unless a requirement explicitly demands it; ticket lifecycle edits to TODO.md are the user's.
## Quality Self-Check (before finishing)
1. Did I evaluate architectural fit before nitpicks?
2. Did I map every ticket requirement to evidence?
3. Are all blocking issues genuinely blocking (not stylistic)?
4. Did I avoid making git writes?
5. Did I update both `<name>.review.md` and `<name>.md`?
Add MCP local stdio integration to Yoi without weakening Worker history, prompt-context, scoped tool permission, or Plugin/Feature layering invariants.
MCP is a protocol-backed integration layer on top of `pod::feature`. `pod::feature` supplies contribution/lifecycle/runtime-discovered registration substrate; MCP owns its own enablement, local server trust model, command/env/secret policy, and MCP-specific permission decisions. MCP is not the Plugin model, and Plugin permission policy is not implemented by feature-layer authority grants.
## Motivation / background
Yoi needs to integrate with external capability providers without turning them into hidden context sources or bypassing ordinary Tool/Worker safety rules. MCP is useful because it can expose tools, resources, and prompts from local protocol servers, but those server-provided declarations and results are untrusted and must be normalized through Yoi's existing authority boundaries.
The first MCP slice should focus on local stdio servers because they are concrete enough to implement and debug while keeping remote auth, OAuth, Streamable HTTP, registry distribution, sampling, and elicitation out of the initial trust boundary.
A configured local MCP server runs as a local executable. Yoi feature authority does not sandbox that executable's OS-level side effects, so command/env/secret handling and explicit local trust policy are MCP-layer responsibilities rather than generic `pod::feature` grants.
## Strategy / design direction
- Baseline the initial implementation on MCP specification `2025-11-25`.
- Start with local stdio MCP servers only.
- Treat MCP server metadata, tools, resources, prompts, and results as untrusted content.
- Do not allow MCP resources/prompts to become hidden context injection.
- They must be explicit tool operations with history records.
- Use the normal Yoi ToolRegistry, PreToolCall permission, history, and bounded result paths.
- Do not add private MCP-only bypasses around Worker/tool invariants.
- Keep sampling and elicitation fail-closed initially.
- Keep Streamable HTTP, remote auth, OAuth, and MCP Registry/distribution out of the first slice.
- Treat local stdio server execution as an explicit MCP config/trust decision, not as a `pod::feature` authority grant.
- Document clearly that a configured local MCP server runs as a local executable; Yoi feature authority does not sandbox its OS-level side effects.
### Layering decisions
-`pod::feature` is an API/contribution substrate.
- It owns contribution declarations, provider/service lifecycle hooks, diagnostics, runtime-discovered registration plumbing, and integration with normal Worker/ToolRegistry paths.
- It does not own Plugin permission policy or MCP server trust policy.
- Plugin is a user-facing package/config/runtime layer over `pod::feature`.
- Plugin permissions are Plugin-layer policy.
- Plugin package discovery/enablement must not be conflated with MCP local server execution.
- MCP is a separate feature-backed integration layer.
- MCP enablement, command/env/secret handling, server trust, and MCP-specific permission decisions live in MCP config/implementation.
- MCP provider-discovered tools/resources/prompts are exposed through the feature API and ordinary Yoi tool paths.
### Concrete implementation tickets
Completed prerequisites:
-`00001KTR81P9X` — Extend `pod::feature` API for external protocol-backed capability providers.
4.`00001KVHR3WSD` — MCP tools/call execution through ordinary Tool path.
- PreToolCall gate before server call, bounded result serialization, history path.
5.`00001KVHR3WSN` — MCP resources/prompts as explicit tool operations.
- resources/list/read and prompts/list/get without hidden context injection.
6.`00001KVHR3WSW` — MCP list_changed notification handling.
- deterministic safe refresh/diagnostic behavior without breaking tool schema or prompt-cache invariants.
The old broad implementation Ticket `00001KTR82RB7` is superseded by this sequence and should not be used as an implementation work item.
### Terminology
Use `runtime-discovered` or `provider-discovered` for MCP tools/resources/prompts discovered from `tools/list`, `resources/list`, or `prompts/list`. Avoid `dynamic tools` / `dynamic registry` in new MCP design prose because those phrases imply that model-visible tool schemas may change during an active LLM run.
The intended invariant is:
```text
provider-discovered at startup / provider initialization;
registered into the ordinary ToolRegistry before model exposure;
run-stable for the duration of a model request/run;
refreshed only at a safe boundary or reported as a diagnostic.
```
### Later follow-ups
- Richer MCP task/task-support integration if ordinary tool-call fallback is insufficient.
- Streamable HTTP transport.
- OAuth / remote auth.
- Registry/package distribution.
- Explicit MCP/Plugin bridge only if separately approved; do not conflate Plugin packages with MCP local server execution.
## Success criteria / exit conditions
- A local mock MCP server can be configured explicitly and initialized.
- Discovered MCP tools appear as ordinary Yoi tools with stable namespacing.
- Tool calls go through ordinary permission and history paths.
- MCP resources/prompts are explicit operations, not hidden context injections.
- MCP result forms are bounded and safely serialized.
- Secret values, command/env details, and server diagnostics are redacted where required.
- Local server trust boundary is documented: Yoi does not sandbox the configured executable through feature authority.
- Feature, Plugin, and MCP permission/trust responsibilities are documented as separate layers.
## Decision context
- MCP is not the Plugin model; it is a protocol-backed integration layer using `pod::feature` substrate.
-`pod::feature` should provide contribution/lifecycle/runtime-discovered registration plumbing, not MCP server trust policy or Plugin package permission policy.
- MCP resources and prompts must never be hidden context injection. They are explicit operations recorded through ordinary history/tool paths.
- Provider-discovered tools are discovered at startup/provider initialization and registered before model exposure; model-visible schemas remain run-stable during a request/run.
- Local stdio server execution is a user/config trust decision. Yoi does not sandbox the local executable merely because it is configured through MCP.
Build Yoi's Plugin platform as a coherent extension system: packages are discovered and inspected safely, enabled explicitly, registered through typed Plugin surfaces, executed in a sandboxed runtime, constrained by Plugin-layer grants, and authored through SDK/templates rather than raw runtime ABI details.
The long-term platform goal is not merely to run Wasm. It is to make Plugin packages a durable, inspectable, permissioned, and authorable extension layer for Tools first, then host APIs (`https`, `fs`), and later Service / Ingress surfaces when concrete needs justify them.
## Motivation / background
The current Plugin foundation is already substantial:
- package discovery and explicit enablement resolver;
- Tool surface registration through the ordinary ToolRegistry/model-visible schema path;
- minimal sandboxed WASM Tool execution;
- Plugin permission grant enforcement;
- follow-up Tickets for read-only inspection CLI, `https`, `fs`, and Component Model migration.
The remaining work must be kept as one roadmap because the pieces constrain each other:
- Plugin authoring needs an SDK/PDK and examples, not raw pointer/length Wasm ABI hand-coding.
-`https` and `fs` host APIs must be grant-gated and shaped so they can move cleanly to typed Component Model interfaces.
- Diagnostics (`yoi plugin list/show`) are needed before the system becomes harder to debug.
- Component Model adoption should guide new host API design before a custom raw ABI becomes entrenched.
- Service / Ingress are useful for bridge-style integrations, but should come after Tool runtime, diagnostics, and host API policy are stable.
Research of common Wasm extension systems points to the same pattern: mature systems combine a package manifest, explicit capabilities, a sandbox runtime, host-provided capability APIs, language SDK/PDK bindings, templates/examples, inspection/check tooling, and versioned interfaces.
## Strategy / design direction
- Keep Plugin as a user-facing package/config/runtime layer above lower-level `pod::feature` substrate.
- Plugin owns package discovery, enablement, grant policy, runtime selection, authoring UX, and user-facing diagnostics.
- Preserve authority boundaries.
- Package discovery is read-only inventory.
- Package presence never registers a Tool/Hook, executes Wasm, starts a Service, reads files, opens network, or injects context.
- Explicit enablement and Plugin grants are required before registration/execution/host API use.
- Tool calls/results continue through ordinary ToolRegistry and Worker history paths.
- Treat Component Model as the active Plugin runtime shape before public release.
- New typed Plugin host APIs should be designed in WIT-compatible terms.
-`runtime.kind = "wasm-component"` is the current Plugin runtime authority for new work.
- The earlier `yoi-plugin-wasm-1` raw core-Wasm compatibility bridge is retired from the active roadmap because Plugin has not been publicly released and compatibility would preserve the wrong boundary.
10. Service / Ingress runtime is developed as host-managed lifecycle, event queue, output command, and diagnostics slices.
11. WebSocket support for long-lived integrations is host-owned connection driver + ingress event delivery + output command, not Plugin-owned polling with `recv(timeout)`.
- Keep Discord-style bridge goals split into two stages.
- Outbound Discord/webhook Tool is possible after `https`.
- Bidirectional Discord bridge requires Service + Ingress + WebSocket or inbound HTTP and host routing policy.
## Current implementation split
The broad Plugin runtime redesign is tracked by `00001KVXHVCR5` as context only; implementation should proceed through concrete Tickets instead of routing that umbrella as a single coding task.
1.`00001KVXK0WD3` Remove legacy raw WASM Plugin runtime.
- Deletes the active `LegacyToolAdapter` / raw-Wasm execution path.
2.`00001KVXK0WDH` Reject legacy Plugin runtime in manifest and CLI diagnostics.
- Makes `plugin.toml`, `yoi plugin check/list/show`, docs, and fixtures reflect Component Model only runtime authority.
3.`00001KVXK0WDQ` Define Plugin Service lifecycle and ingress queue runtime.
- Adds host-managed lifecycle, bounded queue, serial dispatch, backpressure, timeout, and diagnostics.
4.`00001KVXK0WDX` Add Plugin service output command model.
- Lets service handlers request side effects as grant-checked commands rather than ambient authority.
5.`00001KVXK0WE4` Add host-owned WebSocket driver for Plugin services.
- Converts incoming WS frames into ingress events and sends outbound frames through output commands.
6.`00001KVXK0WEA` Update Plugin WIT PDK templates for service event runtime.
- Aligns authoring API, WIT, PDK, templates, and docs with the new event/command execution model.
## Success criteria / exit conditions
- Users can inspect Plugin discovery/enablement/grant/runtime state through a read-only CLI without executing Plugin code.
- Plugin authors can build a Tool Plugin without writing raw memory/pointer ABI plumbing.
- Tool Plugins can safely call grant-gated `https` and `fs` host APIs.
- Component Model is the only active Plugin runtime path, with WIT-compatible host API types and measured packaging/runtime impact.
- Plugin grants remain authoritative over registration, execution, and host API calls.
- Raw core-Wasm Plugin compatibility is removed before public release; tests and docs no longer treat it as a current runtime.
- Documentation covers package format, Component Model runtime, host API authority, authoring SDK/templates, Service/Ingress event runtime, and operational debugging.
- Service/Ingress work is host-managed: services have lifecycle/status, ingress uses bounded queues, side effects are output commands, and WebSocket integrations use host-owned connection drivers.
## Decision context
- This Objective is roadmap context, not Ticket authority. Implementation still requires reading concrete Ticket bodies, threads, artifacts, and relations.
- Component Model direction supersedes Yoi's custom raw ABI as both long-term and current active Plugin runtime authority before public release.
-`https` / `fs` work should avoid choices that conflict with later WIT typed interfaces.
- Guest SDK work targets Component Model directly; raw ABI wrappers are not a supported transitional authoring path.
- Plugin and MCP remain separate. Component Model adoption for Plugin does not imply MCP server execution, MCP prompt/resource injection, or MCP trust policy changes.
- Plugin surfaces remain Tool / Hook / Service / Ingress; outbound side effects are Tool metadata and host API grants, not a separate surface.
OSS として Control plane、Runner、Web frontend、protocol を公開しつつ、managed service では hosted control plane、runner fleet、リソース柔軟性、team auth、backup、audit、availability、multi-tenant operations で価値を出す。
## Strategy / design direction
### 1. Control plane を先に作る
Team Workspace の正本は server-side control plane に置く。`.yoi` は local backend、single-user/self-hosted compatibility、offline/export/import、runner-local projection、migration bridge として残せるが、multi-user SaaS の正本とはみなさない。
Control plane は Ticket、Objective、Memory、Knowledge、Run、Artifact、Actor、Permission、Audit、Runner state を管理する。Web UI、CLI、TUI、将来の desktop client は、この Control plane を操作する client であり、別の正本 store を持たない。
The old Auto Maintain workflow is retired and removed.
Resolution:
- Deleted `.yoi/workflow/auto-maintain.md`.
- Closed this Ticket as superseded by the newer Ticket-based orchestration workflow split:
-`ticket-intake-workflow`
-`ticket-orchestrator-routing`
-`ticket-preflight-workflow`
-`multi-agent-workflow`
- Updated `multi-agent-workflow` to point to Ticket Intake / Orchestrator Routing / Preflight instead of `$user/auto-maintain`.
- Updated `ticket-intake-workflow` to remove the obsolete auto-maintain connection.
- Updated `prompt-eval-metrics` so future prompt/workflow evaluation targets the current Ticket workflows or worktree workflow instead of `/auto-maintain`.
Rationale:
`auto-maintain` had become a broad and unstable WIP workflow with old assumptions around TODO/tickets and maintenance loops. Keeping it resident risks encouraging large implicit automation and bypassing the clearer gates now provided by Ticket Intake, Ticket Orchestrator Routing, Ticket Preflight, and Multi-agent Worktree Workflow.
Future maintainer/scheduler/lease behavior should be designed as explicit follow-up work, not revived through the deleted auto-maintain workflow.
Validation:
-`git diff --check`
-`./tickets.sh doctor`
- open workflow/docs search no longer finds `auto-maintain` references outside this closed historical Ticket context.
Migrated from tickets/auto-maintain-workflow.md. No legacy review file was present at migration time.
---
<!-- event: close author: hare at: 2026-06-05T15:56:29Z status: closed -->
## Closed
The old Auto Maintain workflow is retired and removed.
Resolution:
- Deleted `.yoi/workflow/auto-maintain.md`.
- Closed this Ticket as superseded by the newer Ticket-based orchestration workflow split:
-`ticket-intake-workflow`
-`ticket-orchestrator-routing`
-`ticket-preflight-workflow`
-`multi-agent-workflow`
- Updated `multi-agent-workflow` to point to Ticket Intake / Orchestrator Routing / Preflight instead of `$user/auto-maintain`.
- Updated `ticket-intake-workflow` to remove the obsolete auto-maintain connection.
- Updated `prompt-eval-metrics` so future prompt/workflow evaluation targets the current Ticket workflows or worktree workflow instead of `/auto-maintain`.
Rationale:
`auto-maintain` had become a broad and unstable WIP workflow with old assumptions around TODO/tickets and maintenance loops. Keeping it resident risks encouraging large implicit automation and bypassing the clearer gates now provided by Ticket Intake, Ticket Orchestrator Routing, Ticket Preflight, and Multi-agent Worktree Workflow.
Future maintainer/scheduler/lease behavior should be designed as explicit follow-up work, not revived through the deleted auto-maintain workflow.
Validation:
-`git diff --check`
-`./tickets.sh doctor`
- open workflow/docs search no longer finds `auto-maintain` references outside this closed historical Ticket context.
- Re-reviewed the fix commit `b30b43b9 test: cfg-gate e2e observer payloads` after the earlier request-changes review.
- Inspected the updated observer module boundary and call sites in `crates/tui/src/lib.rs` and `crates/tui/src/multi_pod.rs`, plus the unchanged harness/tests in `tests/e2e`.
Evidence:
-`e2e_observer` is now only compiled from `crates/tui/src/lib.rs` under `#[cfg(feature = "e2e-test")]`; the previous normal-build no-op facade was removed.
- Observer payload construction is gated at call sites with `#[cfg(feature = "e2e-test")]`, including `panel_ready`, `selection_changed`, `action_requested`, `quit_requested`, and `emit_rows_rendered` calls.
- Panel E2E DTOs/helpers (`PanelE2eRowKey`, `PanelE2eRect`, `PanelE2eRenderedRow`, `PanelE2eRowsRendered`, `App::emit_rows_rendered`) are now behind `#[cfg(feature = "e2e-test")]`, so the normal panel render path no longer builds row snapshots or retains that runtime helper path.
- The background-task hold seam is still feature-gated: `check_background_task_hold` and `release_background_task_hold` calls are under `#[cfg(feature = "e2e-test")]`, and `YOI_TUI_TEST_HOLD_BACKGROUND_TASK` behavior lives in the gated observer module.
- Mouse capture tracking remains intact in the harness: it tracks `?1000h` and `?1006h`, `click(...)` requires both capture modes before injecting PTY bytes, the test waits for rendered rows, asserts `selection_changed`, and asserts no `action_requested` dispatch.
- Quit-latency coverage remains intact: the test waits for `panel_ready`, then verifies an actual pending `reload` background-task barrier before sending Ctrl+C through the PTY and asserting bounded exit.
- The production/non-production boundary now satisfies the Ticket intent: the harness remains opt-in, observability is read-only and feature-gated, and no UI input/action path is bypassed.
Validation run in `/home/hare/Projects/yoi/.worktree/e2e-harness`:
- Inspected Ticket record and `git diff 134e8b8b..HEAD` for commits `96561897` and `10a1c383`.
-`tests/e2e` provides a credible first declarative harness (`PanelHarness::spawn`, `wait_for`, `wait_for_rows`, `click`, `press`, `expect_selection`, `expect_exit_within`, artifacts/metadata/input/output/event logs). This is not merely a fixed-sleep shell script.
- Mouse-selection scenario waits for rendered rows, verifies both normal mouse and SGR mouse capture before `click`, sends the click through PTY bytes, waits for `selection_changed`, and asserts no `action_requested` dispatch.
- Quit-latency scenario creates a real feature-gated background-task hold barrier, waits until the task is actually waiting before sending Ctrl+C through the PTY, and measures bounded exit latency.
-`yoi-e2e` is opt-in via package feature/test `required-features = ["e2e"]`; e2e tests are outside default members. `YOI_TUI_TEST_EVENTS` and `YOI_TUI_TEST_HOLD_BACKGROUND_TASK` env behavior is behind `tui/e2e-test` / `yoi/e2e-test` feature gates, and the hook is observability-only.
Required change:
- The normal production build still contains/evaluates too much e2e harness glue. In non-`e2e-test` builds, `crates/tui/src/e2e_observer.rs` exposes no-op `emit`/hold functions, but call sites still execute test-specific data construction. In particular `App::emit_rows_rendered` and its panel row key/rect DTOs are compiled unconditionally and `app.emit_rows_rendered()` is called from the panel render path, causing row snapshots to be built every draw even though emission is a no-op. Selection/action/quit call sites also construct `serde_json::json!` payloads before the no-op facade. This violates the recorded boundary that production binaries should not contain harness logic and production-side hooks must be feature-gated/compiled out for normal builds.
- Please cfg-gate the call sites/helpers/DTOs, or use a lazy cfg-gated macro/helper so normal builds do not evaluate or retain e2e event payload construction. A tiny compile-only facade is acceptable only if it does not execute or allocate e2e-specific work and does not keep harness DTO logic in the normal runtime path.
Validation run in `/home/hare/Projects/yoi/.worktree/e2e-harness`:
Coder first-slice review: changes requested before external review.
The initial E2E harness direction is promising, but it does not yet satisfy the regression-prevention bar from this Ticket.
Required changes sent to Coder:
1. Mouse E2E must not pass solely by injecting raw SGR mouse bytes. Harness must track terminal mouse capture enable output (`?1000h` / `?1006h` equivalent) and fail `click(...)` / expose `expect_mouse_capture_enabled()` when capture was not observed.
2. Production binary contamination must be reduced. The TUI JSONL observer currently appears to be available in normal builds via `YOI_TUI_TEST_EVENTS`; prefer explicit `e2e-test` feature/cfg gating so release/normal binary has the hook compiled out. Report if feature gating is too large.
3. Quit latency E2E must wait for a real pending/background-work barrier at the moment of quit, not merely assert that `background_task_started` happened sometime earlier. Strengthen the scenario to prove Ctrl+C while pending work exists exits promptly.
4. Update implementation_report and validation evidence after changes.
Reason:
- The mouse selection regression specifically needs to catch missing terminal mouse capture, which raw SGR injection can bypass.
- The user explicitly requested a structure where E2E harness logic does not mix into the production binary.
- The quit latency regression needs measured user-visible behavior under a synchronized pending-work condition, not a loose startup smoke test.
---
<!-- event: implementation_report author: hare at: 2026-06-13T14:38:03Z -->
## Implementation report
Implemented an opt-in E2E testing foundation for real `yoi panel` process automation.
API / harness shape:
- Added workspace package `tests/e2e` (`yoi-e2e`) with required feature `e2e`, so scenarios are opt-in.
- Harness drives the real `yoi panel` binary through a Unix PTY and sends SGR mouse / keyboard bytes through that PTY only.
- Harness captures artifacts under a per-run artifact directory: `events.jsonl`, `input.log`, `pty-output.log`, and `run.json`.
Production / non-production boundary:
- Harness logic stays in `tests/e2e` and is not mixed into production crates.
- Production-side change is limited to an opt-in read-only TUI JSONL observer enabled only by `YOI_TUI_TEST_EVENTS`.
- Observer records Panel/TUI synchronization and assertion events (`panel_ready`, `rows_rendered`, `selection_changed`, `mouse_click`, `action_requested`, `quit_requested`, background task lifecycle, terminal cleanup, exit).
- The observer does not mutate UI state, inject input, bypass actions, or grant authority; real input remains PTY-only.
Scenarios added:
- Panel mouse selection regression: waits for rendered rows, sends an SGR mouse click through PTY, asserts selection changed, and asserts no panel action was dispatched.
- Panel quit latency regression: waits for Panel ready plus background-task barrier, sends Ctrl+C through PTY, asserts clean exit within the threshold, and verifies the quit event.
- The first slice is Unix PTY-based; cross-platform PTY support is not implemented.
- The screen artifact is currently raw PTY output rather than a parsed terminal snapshot.
- Fixture setup blocks Companion/Orchestrator role auto-spawn by placing intentionally invalid isolated test pod metadata in the fixture data dir; this avoids leaking live background Pods but should be replaced by a cleaner fixture strategy if the Panel gains an explicit test fixture mode.
---
<!-- event: implementation_report author: hare at: 2026-06-13T15:00:29Z -->
## Implementation report
Follow-up implementation update for review-required changes:
Mouse E2E false-positive prevention:
-`PanelHarness` now tracks the PTY output for terminal mouse mode enable/disable escape sequences.
- It requires both normal mouse tracking (`ESC[?1000h`, with disable `ESC[?1000l`) and SGR mouse mode (`ESC[?1006h`, with disable `ESC[?1006l`) to have been observed as currently enabled.
- Added `PanelHarness::expect_mouse_capture_enabled()` and the Panel mouse E2E calls it before clicking.
-`PanelHarness::click(...)` also fails with `MouseCaptureNotEnabled` if mouse capture has not been observed, so raw SGR mouse injection alone cannot make the test pass.
Production binary contamination reduction:
- Added explicit feature gates:
-`tui/e2e-test`
-`yoi/e2e-test = ["tui/e2e-test"]`
- The `YOI_TUI_TEST_EVENTS` JSONL event path and the background-task hold seam are compiled only under `tui/e2e-test`.
- Normal builds still type-check a no-op facade so call sites stay simple, but env-var observability/hold behavior is compiled out unless the feature is enabled.
- Added a feature-gated test-only seam `YOI_TUI_TEST_HOLD_BACKGROUND_TASK=reload` that holds the Panel reload task pending in the E2E binary until quit aborts it.
- Added `PanelHarness::expect_background_task_pending("reload")`, which verifies that a `background_task_started` event exists and no matching `background_task_finished` or `background_task_aborted` event has superseded it.
- The quit latency test now uses the hold seam and asserts pending reload work immediately before sending Ctrl+C through the PTY.
Harness/artifact adjustment:
- E2E fixture artifacts now persist under workspace `target/e2e-artifacts/...` instead of package-local temporary dirs, so failure artifacts remain inspectable.
- The screen artifact remains raw PTY output, not a parsed terminal snapshot.
- The test-only pending-task hold intentionally changes timing only for the `e2e-test` binary; it is not compiled into normal builds.
---
<!-- event: review author: hare at: 2026-06-13T15:05:52Z status: request_changes -->
## Review: request changes
Request changes.
Evidence reviewed:
- Inspected Ticket record and `git diff 134e8b8b..HEAD` for commits `96561897` and `10a1c383`.
-`tests/e2e` provides a credible first declarative harness (`PanelHarness::spawn`, `wait_for`, `wait_for_rows`, `click`, `press`, `expect_selection`, `expect_exit_within`, artifacts/metadata/input/output/event logs). This is not merely a fixed-sleep shell script.
- Mouse-selection scenario waits for rendered rows, verifies both normal mouse and SGR mouse capture before `click`, sends the click through PTY bytes, waits for `selection_changed`, and asserts no `action_requested` dispatch.
- Quit-latency scenario creates a real feature-gated background-task hold barrier, waits until the task is actually waiting before sending Ctrl+C through the PTY, and measures bounded exit latency.
-`yoi-e2e` is opt-in via package feature/test `required-features = ["e2e"]`; e2e tests are outside default members. `YOI_TUI_TEST_EVENTS` and `YOI_TUI_TEST_HOLD_BACKGROUND_TASK` env behavior is behind `tui/e2e-test` / `yoi/e2e-test` feature gates, and the hook is observability-only.
Required change:
- The normal production build still contains/evaluates too much e2e harness glue. In non-`e2e-test` builds, `crates/tui/src/e2e_observer.rs` exposes no-op `emit`/hold functions, but call sites still execute test-specific data construction. In particular `App::emit_rows_rendered` and its panel row key/rect DTOs are compiled unconditionally and `app.emit_rows_rendered()` is called from the panel render path, causing row snapshots to be built every draw even though emission is a no-op. Selection/action/quit call sites also construct `serde_json::json!` payloads before the no-op facade. This violates the recorded boundary that production binaries should not contain harness logic and production-side hooks must be feature-gated/compiled out for normal builds.
- Please cfg-gate the call sites/helpers/DTOs, or use a lazy cfg-gated macro/helper so normal builds do not evaluate or retain e2e event payload construction. A tiny compile-only facade is acceptable only if it does not execute or allocate e2e-specific work and does not keep harness DTO logic in the normal runtime path.
Validation run in `/home/hare/Projects/yoi/.worktree/e2e-harness`:
- Removed the normal-build no-op `e2e_observer` module path and now compile the observer module only behind `tui/e2e-test` via `#[cfg(feature = "e2e-test")] mod e2e_observer;`.
- Added `#[cfg(feature = "e2e-test")]` to all Panel/TUI observability call sites that build JSON payloads, including terminal cleanup/exit, panel ready, rows-rendered, mouse click, selection changes, action requests, quit request, and background task lifecycle events.
-`App::emit_rows_rendered`, `PanelE2eRowKey`, `PanelE2eRect`, `PanelE2eRenderedRow`, `PanelE2eRowsRendered`, and the conversion helpers are now compiled only with `tui/e2e-test`.
- Normal builds no longer call `app.emit_rows_rendered()`, no longer evaluate `serde_json::json!` e2e payloads, and no longer retain the Panel E2E DTO/helper logic in the runtime path.
- The background reload hold seam remains compiled/called only under `tui/e2e-test`; `YOI_TUI_TEST_EVENTS` and `YOI_TUI_TEST_HOLD_BACKGROUND_TASK` behavior remains feature-gated.
Preserved E2E behavior:
- Mouse E2E still verifies PTY output for normal mouse tracking + SGR mouse enable sequences before any raw SGR click can be sent.
-`PanelHarness::click(...)` still fails if mouse capture was not observed.
- Quit latency E2E still uses the feature-gated pending reload hold barrier and asserts the reload task is pending before Ctrl+C.
- The screen artifact remains raw PTY output rather than a parsed terminal snapshot.
---
<!-- event: review author: hare at: 2026-06-13T15:21:30Z status: approve -->
## Review: approve
Approve.
Delta reviewed:
- Re-reviewed the fix commit `b30b43b9 test: cfg-gate e2e observer payloads` after the earlier request-changes review.
- Inspected the updated observer module boundary and call sites in `crates/tui/src/lib.rs` and `crates/tui/src/multi_pod.rs`, plus the unchanged harness/tests in `tests/e2e`.
Evidence:
-`e2e_observer` is now only compiled from `crates/tui/src/lib.rs` under `#[cfg(feature = "e2e-test")]`; the previous normal-build no-op facade was removed.
- Observer payload construction is gated at call sites with `#[cfg(feature = "e2e-test")]`, including `panel_ready`, `selection_changed`, `action_requested`, `quit_requested`, and `emit_rows_rendered` calls.
- Panel E2E DTOs/helpers (`PanelE2eRowKey`, `PanelE2eRect`, `PanelE2eRenderedRow`, `PanelE2eRowsRendered`, `App::emit_rows_rendered`) are now behind `#[cfg(feature = "e2e-test")]`, so the normal panel render path no longer builds row snapshots or retains that runtime helper path.
- The background-task hold seam is still feature-gated: `check_background_task_hold` and `release_background_task_hold` calls are under `#[cfg(feature = "e2e-test")]`, and `YOI_TUI_TEST_HOLD_BACKGROUND_TASK` behavior lives in the gated observer module.
- Mouse capture tracking remains intact in the harness: it tracks `?1000h` and `?1006h`, `click(...)` requires both capture modes before injecting PTY bytes, the test waits for rendered rows, asserts `selection_changed`, and asserts no `action_requested` dispatch.
- Quit-latency coverage remains intact: the test waits for `panel_ready`, then verifies an actual pending `reload` background-task barrier before sending Ctrl+C through the PTY and asserting bounded exit.
- The production/non-production boundary now satisfies the Ticket intent: the harness remains opt-in, observability is read-only and feature-gated, and no UI input/action path is bypassed.
Validation run in `/home/hare/Projects/yoi/.worktree/e2e-harness`:
- Screen artifact is raw PTY output rather than parsed terminal snapshot。
- This is a first vertical slice for Panel/TUI PTY E2E; broader Pod protocol/provider stub scenarios remain future work under this E2E harness direction。
Next:
- Mark Ticket `done` and clean up child coder/reviewer Pods plus implementation worktree/branch. Closure remains separate.
---
<!-- event: state_changed author: orchestrator at: 2026-06-13T15:23:44Z from: inprogress to: done reason: merged_and_validated field: state -->
## State changed
E2E harness implementation branch was reviewed, approved after requested production-boundary changes, merged into the Orchestrator branch as `b3bd6b11`, and validated in the Orchestrator worktree. Opt-in Panel PTY E2E tests for mouse selection and quit latency passed, along with formatting, diff check, normal/e2e feature package checks, and E2E package check. Ticket implementation work is done; closure remains separate.
Follow-up requested by user: E2E harness should build the current `yoi` binary itself instead of relying on a prebuilt `YOI_E2E_BIN` / inferred `target/debug/yoi`.
Required correction:
- Default E2E binary provider should run `cargo build -p yoi --features e2e-test --bin yoi` from the workspace root at test time, then spawn the resulting `target/{profile}/yoi` directly through PTY.
-`YOI_E2E_BIN` may remain as an explicit override, but normal arbitrary `cargo test -p yoi-e2e --features e2e ...` should use a freshly built binary without requiring a separate manual build step.
- Do not use `cargo run` as the process-under-test because that would put Cargo in the PTY/signal/quit-latency measurement path.
- Preserve the existing production/non-production boundary and E2E feature gating.
---
<!-- event: state_changed author: hare at: 2026-06-13T16:34:06Z from: done to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-13T16:34:06Z status: closed -->
Closed as no longer needed. Arbitrary-turn Pod/session fork and multi-turn rewind are not part of the current desired workflow; current restore/rewind/fork behavior is sufficient for active use, and future history editing should be reopened as a narrower current-runtime design if needed.
Migrated from tickets/pod-session-fork.md. No legacy review file was present at migration time.
---
<!-- event: state_changed author: hare at: 2026-06-20T16:31:28Z from: planning to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-20T16:31:29Z status: closed -->
## 完了
Closed as no longer needed. Arbitrary-turn Pod/session fork and multi-turn rewind are not part of the current desired workflow; current restore/rewind/fork behavior is sufficient for active use, and future history editing should be reopened as a narrower current-runtime design if needed.
Closed as superseded/no longer needed. Workflow evaluation metrics and improvement planning have moved under the newer Memory redesign / team-workspace memory objectives, so this old prompt/workflow metrics ticket should not remain as a standalone planning item.
Migrated from tickets/prompt-eval-metrics.md. No legacy review file was present at migration time.
---
<!-- event: state_changed author: hare at: 2026-06-20T16:31:29Z from: planning to: closed reason: closed field: state -->
## State changed
Ticket を closed にしました。
---
<!-- event: close author: hare at: 2026-06-20T16:31:29Z status: closed -->
## 完了
Closed as superseded/no longer needed. Workflow evaluation metrics and improvement planning have moved under the newer Memory redesign / team-workspace memory objectives, so this old prompt/workflow metrics ticket should not remain as a standalone planning item.
Migrated from tickets/tui-picker-live-pending-pods.md. No legacy review file was present at migration time.
---
<!-- event: plan author: hare at: 2026-05-30T04:54:03Z -->
## Plan
## Preflight implementation plan
Classification: implementation-ready.
No blocking preflight gap remains. The product rule is settled: reachable live Pods must be visible/attachable even if durable session-log metadata is incomplete, but missing session logs must not make them restorable.
Implementation detail to preserve:
- Treat “pending live” as a display/model condition, not persisted state.
- Use reachable `LivePodInfo` plus incomplete stored/session summary or runtime-only segment id to improve row order/preview/debug ids.
- Do not mark the Pod restorable unless stored metadata has a usable active segment/session under existing restore rules.
Current code map:
-`crates/tui/src/picker.rs`: picker construction, row rendering, live attach socket override.
-`crates/tui/src/pod_list.rs`: shared model merge/sort/truncation/actions; current sort is updated_at desc only; `merge_live` already supplements segment id from runtime.
-`crates/tui/src/main.rs`: selected live row attaches via socket override before restore fallback.
-`crates/tui/src/multi_pod.rs`: also uses `PodList`, so ordering effects should be checked.
-`crates/pod/src/discovery.rs`: List/Attach/Restore behavior is related but out of scope.
-`crates/pod-registry/src/table.rs`: runtime allocation segment id source.
-`crates/pod-store/src/lib.rs`: pending active segment metadata; do not persist runtime supplementation.
Implementation phases:
1. Change `PodList::from_sources` sorting to reachable-live first, then updated_at desc, then pod_name asc; truncation remains after sorting.
2. Make reachable live pending preview explicit, e.g. `[live, pending segment]`, when durable summary is incomplete.
3. Preserve and test runtime segment id supplementation for display/debug ids only.
4. Add focused `pod_list` tests for live-first-before-truncation, live pending runtime segment attach-only behavior, and live-only runtime segment attach-only behavior.
5. Adjust existing sort/multi-pod tests only as needed.
6. Keep `PodDiscovery::inspect` / `AttachOrRestorePod` behavior out of scope; record follow-up if needed.
Critical risks:
- Live attachability and restoreability must stay separate.
- Do not persist runtime segment supplementation to pod-store.
- Sort must happen before truncation.
- Do not duplicate picker-specific merge/sort logic; fix shared `PodList`.
- Rank reachable live rows, not unreachable registry allocations.
- Preview wording must not imply restoreability.
- Multi-Pod dashboard ordering may change; reviewer should check it remains intended.
Validation plan:
-`cargo test -p tui pod_list`
-`cargo test -p tui picker`
-`cargo test -p tui multi_pod`
-`cargo test -p tui`
-`cargo fmt --check`
---
<!-- event: review author: hare at: 2026-05-30T05:00:32Z status: approve -->
## Review: approve
Approve.
The change correctly moves the live-priority rule into shared `PodList` construction, so both the resume picker and multi-Pod dashboard consume the same merged/sorted model. Reachable live Pods now sort ahead of non-live/unreachable/stopped/corrupt rows before truncation, and live pending rows get display-only runtime segment supplementation plus clearer pending preview text without changing pod-store metadata or restore behavior.
Blocker findings: none.
Requirement coverage:
- Reachable live rows sort before stopped/corrupt/unreachable rows before truncation.
- Sorting remains deterministic inside groups: `updated_at` desc, then pod name asc.
- Live pending/runtime-only rows remain attachable/openable but not restorable.
- Runtime segment id supplementation is display/model-only; no pod-store write path is touched.
- Pending preview uses `[live, pending segment]` and does not imply restoreability.
- Shared `PodList` was fixed rather than duplicating picker-specific logic.
- Unreachable registry allocations are not promoted.
- PodDiscovery / AttachOrRestore behavior was not broadened.
Validation reviewed from coder report:
-`cargo test -p tui pod_list` — passed.
-`cargo test -p tui picker` — passed.
-`cargo test -p tui multi_pod` — passed.
-`cargo test -p tui` — passed.
-`cargo fmt --check` — passed.
Final verdict: approve.
---
<!-- event: close author: hare at: 2026-05-30T05:00:56Z status: closed -->
The old migrated spawned-Pod panel idea has been superseded by the workspace panel, Pod list/open/attach behavior, Ticket role launching, and the local role session registry. The remaining direction is not to revive this standalone spawned-child panel ticket. Future panel work should be tracked through the newer workspace panel / orchestration tickets.
Migrated from tickets/tui-spawned-pod-panel.md. No legacy review file was present at migration time.
---
<!-- event: decision author: hare at: 2026-06-05T04:03:38Z -->
## Decision
Decision: deprioritize this ticket for the current multi-agent system direction.
Current need is not a TUI panel for spawned Pods. The priority is Ticket-driven intake/routing: making Tickets a code-facing durable orchestration record, then exposing Ticket operations to Intake/Orchestrator through a typed backend/tool surface.
This ticket is not closed as technically invalid; it is moved out of the active multi-agent implementation path. Revisit only if direct child Pod visibility/attach UI becomes a concrete UX requirement.
---
<!-- event: state_changed author: hare at: 2026-06-07T03:14:39Z from: intake to: done reason: closed field: workflow_state -->
## State changed
Ticket closed; workflow_state set to done.
---
<!-- event: close author: hare at: 2026-06-07T03:14:39Z status: closed -->
## Closed
Closed as intentionally not planned.
The old migrated spawned-Pod panel idea has been superseded by the workspace panel, Pod list/open/attach behavior, Ticket role launching, and the local role session registry. The remaining direction is not to revive this standalone spawned-child panel ticket. Future panel work should be tracked through the newer workspace panel / orchestration tickets.
{"id":"orch-plan-20260610-090202-1","ticket_id":"00001KSKBPSJG","kind":"accepted_plan","accepted_plan":{"summary":"Implement the model setup wizard as an explicit one-shot CLI path, not normal Pod startup: add `yoi setup-model` that launches a setup TUI. The wizard should select a bundled model/provider entry, optionally accept an auth hint/reference when the provider requires one, and persist a user default Profile by updating the user Profile registry under the normal config root (`profiles.toml` plus a generated user Profile Lua file such as `profiles/default.lua`). It must not write workspace `.yoi`, session history, Ticket files, runtime/local/secret-like files, or start/attach a Pod during setup. Existing normal launch semantics remain unchanged except that subsequent default startup can use the persisted user default Profile.","branch":"tui-model-setup-wizard","worktree":"/home/hare/Projects/yoi/.worktree/tui-model-setup-wizard","role_plan":"Coder implements in `.worktree/tui-model-setup-wizard` with write scope limited to the child worktree. Orchestrator keeps Ticket/progress records in the main workspace. Reviewer will be delegated after coder report."},"author":"orchestrator","at":"2026-06-10T09:02:02Z"}
This is an existing migrated Ticket; no duplicate Ticket was created. The original body remains useful for the desired wizard shape, but several migrated assumptions are stale and must be treated as superseded where they conflict with current Yoi code and project decisions.
### Current request snapshot
Add an interactive TUI setup flow that helps a first-time user choose a provider/model from the provider catalog and persist a user-level default model configuration so a normal fresh `yoi` spawn can resolve a model without manual TOML editing.
### Binding decisions / invariants
- Product entrypoint is the installed `yoi` binary, not an old standalone `tui`/`insomnia` binary. The CLI surface should be chosen under the `yoi` CLI owner boundary; the migrated `tui setup-model` spelling is only historical/placeholder text.
- Current config paths use `manifest::paths::config_dir()` with default `$XDG_CONFIG_HOME/yoi` / `$HOME/.config/yoi`, not `~/.config/insomnia`.
- Normal reusable runtime configuration is Profile-oriented. The implementation must decide, with preflight, whether this wizard writes/updates `profiles.toml`, a profile-local model fragment, or another explicit user config surface; it must not silently reintroduce the removed ambient manifest-cascade model.
- Current catalog auth hints are `AuthHint::None`, `AuthHint::ApiKey`, `AuthHint::SecretRef { ref_ }`, and `AuthHint::CodexOAuth`; the migrated `ApiKey { env: Option<String> }` flow is stale.
- Secret values must not be written into Ticket bodies, logs, diagnostics, or generated artifacts. Prefer the existing local secret-store / `yoi keys` boundary for normal provider credentials; raw `model.auth.file` remains a low-level explicit-file source, not the default UX if a safer secret-ref path is available.
- The wizard must be cancel-safe: no user config/secret writes before explicit confirmation, and failed parsing/writing must leave existing config usable.
### Implementation latitude
- The exact command name may be settled during preflight, but should fit existing `yoi` CLI semantics and help text.
- A simple vertical provider/model list is sufficient for the first implementation; search/filtering can be deferred unless preflight finds the catalog size makes it necessary.
- If only one model exists for the selected provider, skipping the model-choice step is acceptable.
- Preview may show the generated/surgical config change rather than a full diff, as long as overwrite of an existing model/default is explicit.
### Acceptance criteria
- A first-time user can launch the setup flow from the `yoi` CLI without entering normal Pod spawn by accident.
- The flow loads current `provider::catalog::load_providers()` and `load_models()` data, displays provider/model choices, and handles all current `AuthHint` variants.
- The confirmed result persists a user-level default model/profile configuration at the current Yoi config path, without storing plaintext secrets in config by default.
- Existing user config with an existing model/default is detected and requires overwrite confirmation; malformed config is reported and not rewritten.
- Esc/Ctrl-C cancel before confirmation leaves files unchanged.
- After setup, a normal fresh `yoi` spawn can resolve the selected model/profile without a model-resolve error.
- Validation includes focused Rust tests for CLI parsing/config rendering/update behavior and `nix build .#yoi` because this changes CLI/TUI/runtime resources/packaging-visible code.
This Ticket is ready for Orchestrator routing to preflight/spike, not direct implementation. Preflight should first settle the current user-config write target and command spelling, then either update the Ticket body or record a concise binding decision before implementation.
Updated existing Ticket 20260527-000018-tui-user-model-setup in place; no duplicate was created. The migrated request is now clarified as a Yoi CLI/TUI first-run model setup flow, with stale `tui`/`insomnia`/old AuthHint assumptions explicitly superseded in the thread. Routing readiness is spike/preflight, not direct implementation: first settle current `yoi` command spelling and the user-level config write target for Profile-oriented model defaults, then implement the wizard. needs_preflight=true; risk_flags=[cli-ux, profiles-config, secrets, auth-boundary, migration-staleness, tui].
Intake clarification completed for the existing Ticket. The Ticket is ready for Orchestrator routing to preflight/spike; implementation should not begin until the preflight decisions called out in the intake summary are recorded.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-10T07:59:32Z from: ready to: queued reason: queued field: state -->
## State changed
Ticket を `yoi ticket` が queued にしました。
---
<!-- event: decision author: hare at: 2026-06-10T07:59:32Z -->
## Decision
Human authorized Queue from workspace panel notification; Orchestrator read the Ticket and will accept before implementation side effects.
---
<!-- event: state_changed author: "yoi ticket" at: 2026-06-10T07:59:32Z from: queued to: inprogress reason: cli_state field: state -->
## State changed
State changed to `inprogress`.
---
<!-- event: decision author: hare at: 2026-06-10T07:59:32Z -->
## Decision
Accepting implementation after workspace inspection. Expected overlap with prompt-resource cleanup is low, so route in parallel in a dedicated worktree.
---
<!-- event: plan author: orchestrator at: 2026-06-10T09:02:02Z -->
## Plan
Routing decision: implementation_ready
Reason:
- Ticket is already `inprogress`; relation query and orchestration-plan query show no blockers.
- Existing thread marked this as needing a small preflight. Bounded code/context check resolved the implementation-shaping decisions without needing another planning round.
Evidence checked:
- Ticket body/thread/artifacts for `00001KSKBPSJG`.
- Relation records: none.
- Orchestration plan records: none before this routing pass.
- Current worktree/branch state: `.worktree/tui-model-setup-wizard` exists and has no implementation commit yet.
- Current CLI surface in `crates/yoi/src/main.rs` includes top-level `panel`, `keys`, `pod`, `ticket`, `objective`, `session`, `memory lint`; setup is not yet present.
- Profile/config surface in `crates/manifest/src/paths.rs` and `crates/manifest/src/profile.rs`; active reusable Profiles are Lua-first with user registry/config under the normal config root.
- Provider/model catalog resources under `resources/providers` and `resources/models`.
Binding decisions / invariants:
- Add an explicit one-shot setup entry point `yoi setup-model`; it must not run normal Pod startup, create sessions, attach sockets, or spawn Pods while configuring.
- Persist the result as a user default Profile through normal Profile mechanisms: user `profiles.toml` plus a generated user Profile Lua file (for example `profiles/default.lua`) under the normal config root. Do not write workspace `.yoi`, Ticket records, session history, runtime/local/secret-like files, or project records from the setup wizard.
- Do not introduce legacy manifest authority or new environment-variable configuration surfaces.
- Keep existing startup/profile semantics intact; the setup wizard only creates/updates config that normal startup already knows how to consume.
- If provider credential handling needs more than selecting/recording an existing auth hint/ref, escalate rather than inventing a new secret store flow.
Implementation latitude:
- Exact TUI layout and internal state machine organization are up to coder, bounded by the one-shot setup and persistence invariants.
- Coder may choose the generated profile name/path if it is deterministic, user-config scoped, and unambiguous from the default selector.
- Coder may keep the catalog/model choice set modest and testable for the first implementation.
Validation:
- Focused CLI/parser and setup persistence tests.
- Focused TUI/setup module tests where practical.
-`cargo fmt --check` or `cargo fmt`, `git diff --check`, focused cargo tests, and broader `cargo check`/`nix build .#yoi` before merge because this touches CLI/TUI/config/package source surfaces.
Escalate if:
- The implementation requires changing Profile registry semantics, secret-store behavior, normal Pod launch behavior, or workspace `.yoi` authority.
- Credential entry/storage cannot be limited to an existing explicit auth reference/hint.
---
<!-- event: plan author: orchestrator at: 2026-06-10T09:08:59Z -->
- This Ticket remains `inprogress` and has an accepted implementation plan for `.worktree/tui-model-setup-wizard`, but the current Orchestrator process cannot delegate write scope to the child worktree.
- Attempted `SpawnPod` for `coder-tui-model-setup-wizard` with write scope limited to the child worktree failed: requested scope `/home/hare/Projects/yoi/.worktree/tui-model-setup-wizard` is not within the spawner effective scope.
- Retrying with broader `/home/hare/Projects/yoi` write scope also failed for the same effective-scope reason.
- Direct file write to the child worktree through the available tool scope is read-only, so the Orchestrator cannot safely implement the Ticket in this session either.
Evidence checked:
- Ticket body/thread/artifacts and relation/orchestration-plan records.
- Current CLI/profile/provider code map was boundedly inspected and the accepted plan was recorded.
- Worktree `.worktree/tui-model-setup-wizard` exists, but no coder was spawned and no implementation files were changed.
Next action:
- Continue this Ticket from a Pod/session with delegated write scope for `.worktree/tui-model-setup-wizard`, or explicitly re-route/clean up the existing inprogress worktree.
- Do not treat this as planning uncertainty; the current blocker is write/delegation authority for implementation side effects.
-`6bb023e9 merge develop into setup wizard worktree` (brought branch up to current `develop` before implementation)
-`32be6075 feat: add setup model command`
Summary:
- Added a top-level `yoi setup-model` command that enters a one-shot setup path separate from normal Pod startup.
- Added `tui::setup_model` to list catalog-backed models/providers, prompt for a default selection, and persist user Profile config under the normal user config directory.
- Persistence writes `profiles.toml` with `default = "user:default"` and `[profile.default] path = "profiles/default.lua"`, plus generated `profiles/default.lua` using the selected model ref.
- The setup path does not start/attach a Pod, create sessions, or write workspace `.yoi` records.
- Added CLI parser tests for `setup-model` and persistence tests for generated profile config.
Validation run in branch:
-`cargo fmt`
-`cargo test -p tui setup_model --lib` passed.
-`cargo test -p yoi parse_setup_model --bin yoi` passed.
-`cargo check -p yoi` passed.
-`git diff --check` passed.
Notes:
-`nix build .#yoi` was not run in the branch yet; Orchestrator should run it before merge because this touches CLI/TUI/config/package source surfaces.
- The implementation uses a simple bounded terminal setup flow rather than broad TUI refactoring.
The memory linter currently exists as library/pre-write validation used by memory tools, but there is no headless command to check all existing workspace memory/knowledge records at once. This makes it hard to validate `.insomnia/memory` and `.insomnia/knowledge` before commits, migrations, or manual edits.
The installed user-facing binary is currently produced by the `tui` crate as `insomnia`. It is acceptable for this ticket to add the headless lint command to that crate/binary instead of introducing a separate binary. A future rename from `tui` crate to `insomnia`, or a more explicit single-binary CLI structure, can be handled separately.
## Requirements
- Add a headless CLI mode to the existing `insomnia` binary in the `tui` crate.
-`insomnia memory` without `lint` should remain available as a normal positional Pod name if possible.
- If this shape is awkward with the current parser, keep the command unambiguous and document the chosen shape in tests/help text.
- Default workspace root is the current working directory.
-`--workspace <PATH>` overrides the workspace root passed to `memory::WorkspaceLayout::new`.
- Lint all existing records classified by `memory::WorkspaceLayout`:
-`.insomnia/memory/summary.md` when present;
-`.insomnia/memory/decisions/*.md`;
-`.insomnia/memory/requests/*.md`;
-`.insomnia/knowledge/*.md`.
- Do not lint subsystem-owned opaque trees such as `.insomnia/memory/_staging`, `_logs`, `_usage`.
- Use the existing `memory::Linter` and `WriteMode::Update` for existing files so the CLI matches tool pre-write validation semantics without triggering create-only duplicate slug checks on the file itself.
- Print a deterministic, human-readable report by default:
- file path;
- errors;
- warnings;
- summary counts.
- Exit status:
-`0` if no errors, and no warnings when `--warnings-as-errors` is set;
-`1` if lint errors are found, or warnings are found with `--warnings-as-errors`;
-`2` for CLI usage / I/O / unexpected runtime failures.
-`--json` may be simple but should be machine-readable and stable enough for scripts: include workspace, files, errors, warnings, and counts.
- The command must not start a Pod, connect to sockets, enter raw terminal mode, or mutate files.
## Non-goals
- Renaming the `tui` crate to `insomnia`.
- Adding a separate installed binary.
- Linting Workflow files; workflow linting can be a future command.
- Auto-fixing memory/knowledge records.
- Changing memory schema/linter rules.
## Acceptance criteria
-`insomnia memory lint` runs headlessly against the current directory and reports existing memory/knowledge lint results.
-`insomnia memory lint --workspace <PATH>` works in tests/fixtures.
- The command exits non-zero for lint errors.
-`--warnings-as-errors` makes warnings fail.
-`--json` returns valid JSON containing counts and per-file diagnostics.
- Existing Pod/TUI argument parsing behavior remains covered by tests, especially positional Pod names and `--multi`/`--resume` conflicts.
-`cargo fmt --check`, focused `cargo test -p tui` tests, `cargo check -p tui`, `./tickets.sh doctor`, and `git diff --check` pass.
Implemented `insomnia memory lint` as a headless command in the existing user-facing `insomnia` binary. The command lints workspace memory/knowledge records with the existing `memory::Linter` using `WriteMode::Update`, supports human and JSON output, handles warnings-as-errors, preserves `insomnia memory` as a positional Pod name, and returns before TUI/raw-terminal or Pod connection paths. External review approved and validation passed.
Migrated from TODO.md entry without a legacy ticket file. No legacy review file was present at migration time.
---
<!-- event: plan author: hare at: 2026-05-31T00:51:55Z -->
## Plan
Planning note:
- Keep this in the existing user-facing `insomnia` binary implemented by the `tui` crate. Do not add another installed command for this ticket.
- The command should be headless: parse args, lint files, print report, exit. It must not initialize terminal UI or connect to a Pod.
-`insomnia memory lint` is preferred, but `insomnia memory` alone should continue to be a valid Pod-name attach/create path if practical with the current parser.
- Use `memory::Linter` directly so CLI behavior tracks tool pre-write validation. Existing files should be linted with `WriteMode::Update`.
- Keep crate rename / single-binary architecture as future cleanup, not part of this ticket.
---
<!-- event: implementation_report author: hare at: 2026-05-31T02:14:28Z -->
## Implementation report
Implementation report from coder Pod `workspace-memory-lint-coder-20260531`:
- Added `insomnia memory lint [--workspace <PATH>] [--json] [--warnings-as-errors]` as a headless mode in the existing `tui` crate/user-facing `insomnia` binary.
-`insomnia memory` alone remains a positional Pod name.
- The lint command resolves workspace root, collects existing summary/decisions/requests/knowledge records through `memory::WorkspaceLayout`, and lints with existing `memory::Linter` using `WriteMode::Update`.
- The command prints deterministic human output by default and stable JSON with workspace/files/errors/warnings/counts when `--json` is requested.
- Exit codes follow the ticket: 0 clean, 1 lint failures or warnings-as-errors, 2 usage/I/O/output/runtime failures.
- The headless path returns before raw terminal setup or Pod connection/spawn logic.
Validation reported by coder:
-`cargo fmt --check` passed
-`cargo test -p tui memory_lint -- --nocapture` passed
-`cargo test -p tui` passed
-`cargo check -p tui` passed
-`./tickets.sh doctor` passed
-`git diff --check` passed
Unresolved issues: none.
---
<!-- event: review author: hare at: 2026-05-31T02:14:28Z status: approve -->
## Review: approve
External review by reviewer Pod `workspace-memory-lint-reviewer-rerun-20260531`: approve.
The original reviewer Pod `workspace-memory-lint-reviewer-20260531` became non-visible to the parent before output could be recovered; this review was rerun with a replacement read-only reviewer Pod.
Reviewer summary:
- The implementation adds `insomnia memory lint` as a headless mode in the existing user-facing `insomnia` binary.
- The memory lint path branches before raw terminal setup and Pod connection/spawn logic.
- Parser tests preserve `insomnia memory` as positional Pod name behavior.
- The collector targets summary, decisions, requests, and knowledge records while ignoring opaque memory subsystem directories and workflow files.
- Existing `memory::Linter` and `WriteMode::Update` are used, and the code only reads files / writes reports.
- Human and JSON outputs are deterministic enough for the ticket, and exit code mapping matches requirements.
Blockers: none.
Non-blocking follow-ups:
- Add broader fixture coverage for `_staging`, `_usage`, knowledge, and decisions if desired.
- Add process-level exit-code integration tests if a CLI test harness is introduced later.
Validation adequacy: coder-reported validation is sufficient for this ticket. Reviewer additionally checked `git diff --check develop...HEAD` read-only.
---
<!-- event: implementation_report author: hare at: 2026-05-31T02:15:16Z -->
## Implementation report
Main workspace validation after merge:
-`cargo fmt --check` passed
-`cargo test -p tui memory_lint -- --nocapture` passed (10 passed)
-`cargo test -p tui` passed (224 passed)
-`cargo check -p tui` passed with pre-existing dead-code warnings in `llm-worker` and `tui`
-`./tickets.sh doctor` passed
-`git diff --check` passed
---
<!-- event: close author: hare at: 2026-05-31T02:15:17Z status: closed -->
## Closed
Implemented `insomnia memory lint` as a headless command in the existing user-facing `insomnia` binary. The command lints workspace memory/knowledge records with the existing `memory::Linter` using `WriteMode::Update`, supports human and JSON output, handles warnings-as-errors, preserves `insomnia memory` as a positional Pod name, and returns before TUI/raw-terminal or Pod connection paths. External review approved and validation passed.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.