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、Runtime、Web frontend、protocol を公開しつつ、managed service では hosted control plane、runtime 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、local projection、migration bridge として残せるが、multi-user SaaS の正本とはみなさない。
Control plane は Ticket、Objective、Memory、Skill catalog、Artifact、Actor、Permission、Audit、Repository、Runtime / Worker state を管理する。Web UI、CLI、TUI、将来の desktop client は、この Control plane を操作する client であり、別の正本 store を持たない。
- Memory extract redesign is focused on Overview-first extraction and staging resolution.
Source: Objective 00001KVJSMQXZ. Stale when related tickets close.
## Preferences
- User prefers implementation Tickets, not design-only Tickets.
Source: 2026-07-16 session.
## Working assumptions
- Knowledge should be OKF-compatible, while volatile Memory and staging should not be OKF.
Source: architecture objective.
## Reminders
- Re-check extract/consolidation prompts after staging resolution is implemented.
```
Recommended H2 sections:
-`## Current focus`
-`## Preferences`
-`## Working assumptions`
-`## Constraints`
-`## Reminders`
-`## Stale or superseded`
Each item should stay short. When useful, include `Source` and `Stale when` inline. Long explanations, evidence-heavy analysis, durable rationale, citations, and cross-linked concepts should be routed to Knowledge rather than expanded inside Memory.
The single-file layout is an initial storage profile, not an API contract. Workers, Web, Runtime, and CLI should use the Workspace Memory API view rather than depending on the exact file layout, so storage can later split or evolve without changing the model-visible contract.
#### Memory examples
```text
User preference: prefers direct commits only when explicitly requested.
Source: repeated user corrections in sessions around git operations.
Staleness: revisit if user changes repo workflow.
```
```text
Current focus: web Workspace console and Workspace-backed Ticket/Skill authority.
notes:"Untrusted external reference captured as local Objective resource for Memory sensemaking design. This is a summary/extraction for design discussion, not project authority."
---
# Pirolli & Card (2005): The sensemaking process and leverage points for analyst technology
## Citation / source
Peter Pirolli and Stuart Card, PARC. "The Sensemaking Process and Leverage Points for Analyst Technology as Identified Through Cognitive Task Analysis" (2005).
The paper frames intelligence analysis as a sensemaking task:
```text
Information -> Schema -> Insight -> Product
```
The analyst transforms raw data into progressively more structured representations so expertise can apply and so results can be communicated.
The paper's notional data flow is especially relevant to Yoi Memory design:
```text
External data sources
-> shoebox
-> evidence file
-> schemas
-> hypotheses
-> presentation / work product
```
- **External data sources**: raw material, mostly text in the studied setting.
- **Shoebox**: the smaller subset collected as relevant for the task.
- **Evidence file**: extracted snippets / nuggets from the shoebox, plus low-level inferences.
- **Schemas**: re-representations that organize information for analysis.
- **Hypotheses**: tentative conclusions with supporting or disconfirming evidence.
- **Product**: report / presentation / action suited for communication.
## Two major loops
The process has two interacting loops rather than a simple linear pipeline.
### Foraging loop
Activities aimed at finding and selecting information:
- search and filter external data sources;
- collect potentially relevant material into a shoebox;
- read and extract evidence snippets;
- follow up on questions generated by extracted evidence.
The paper highlights the exploration / enrichment / exploitation tradeoff:
- **Exploration**: monitor or search more of the information space; increases recall.
- **Enrichment**: narrow the collected set into smaller, higher-precision subsets.
- **Exploitation**: read/extract/analyze the chosen material more thoroughly.
Analyst tooling can help by changing the cost structure of search, scanning, assessment, selection, attention shifting, and follow-up searches.
### Sensemaking loop
Activities aimed at structuring and reasoning:
- schematize evidence;
- build a case;
- generate / manage hypotheses;
- marshal evidence for and against hypotheses;
- tell a story / produce a report;
- re-evaluate based on feedback or new evidence.
The paper emphasizes opportunistic mixing of bottom-up and top-down processing:
- **Bottom-up**: data triggers schemas, relations, hypotheses, and products.
- **Top-down**: hypotheses / client feedback trigger new searches, re-reading, and re-organization.
## Leverage points
### Foraging loop leverage
- Cost structure of exploration / enrichment / exploitation.
- Cost structure of scanning, recognizing, and selecting items for attention.
- Cost of shifting attentional control to a new domain or task.
- Cost of follow-up searches generated by extracted information.
- Broad-band low-fidelity assessment plus narrow-band high-fidelity processing is a useful design pattern.
### Sensemaking loop leverage
- Span of attention for evidence, hypotheses, and evidentiary relations.
- Generation of alternative hypotheses.
- Confirmation bias and failure to seek disconfirming evidence.
- External representations can expand working memory for evidence/hypothesis structures.
- Tools should help distribute attention toward diagnostic evidence and disconfirming relations.
## Implications for Yoi Memory Objective
This paper directly supports the Objective's direction that effective Memory is not just durable storage or retrieval count.
Useful design implications:
1.**Task-bound shoebox**
- For each Ticket / Objective / question, provide a bounded collection of potentially relevant materials.
- Include provenance and why each item was collected.
2.**Evidence file**
- Extract snippets from source material with source, applicability, confidence, and context.
- Evidence should be usable for support and disconfirmation, not only recall.
3.**Schema / representation layer**
- The system should help create intermediate structures: timelines, entity/relation maps, alternatives, checklists, hypothesis spaces, decision tables.
- These are separate from raw Memory records.
4.**Hypotheses and alternatives**
- Record competing explanations, rejected alternatives, open questions, and evidence gaps.
- Avoid only storing final decisions.
5.**Disconfirming evidence**
- Reviewer / Orchestrator support should explicitly search for contradiction and diagnostic evidence.
- This belongs in Skill guidance and typed review tooling, not in a separate Knowledge store.
- Avoid treating exposure/retrieval counts alone as success.
7.**Product connection**
- The loop should end in a product: Ticket decision, implementation report, review, design doc, Skill update, or validated artifact.
- Memory that never reaches a product is likely a graveyard.
## Boundary with current Yoi direction
- Knowledge as a separate record kind is being removed; this paper's reusable representations should map to Memory artifacts, Ticket artifacts, maintained docs, or Skills depending on authority.
- Workflow tracking is being removed; procedural guidance such as "generate alternatives" or "seek disconfirming evidence" should live in Skills / role prompts and be enforced by typed tools where authority is needed.
- Workspace backend should eventually be the authority for task-bound shoebox / evidence artifacts when Memory moves from local compatibility storage to control-plane records.
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.
`RunResult::RolledBack` should not be reused for this idle control operation. It remains the run-lifecycle signal for submit-time empty-turn rollback.
## Implementation notes
- Target identity can initially be current segment + entry index:
```rust
RewindTargetId{
segment_id: SegmentId,
user_input_entry_index: usize,
}
```
- Include `expected_head_entries` to reject stale picker selections.
- Each target should include:
- preview
- original `Vec<Segment>`
- turn/index metadata if available
- whether the target is eligible
- disabled/warning reason if relevant
- the entry count to truncate to, which is before the selected user message.
- Rewind apply must keep these in sync:
- worker history
-`user_segments`
- session store segment log
-`SegmentLogSink` mirror
- usage history / trackers
- TUI view reconstructed from returned entries
- If a complete current-state reconstruction from log is simpler and safer than maintaining many historical snapshots, prefer that over fragile partial truncation.
## Acceptance criteria
-`:rewind` opens a picker of past user messages by replacing the normal conversation/history view area, not by drawing a small popup.
-`Ctrl+R` opens the same picker only while Pod status is `Idle` or `Paused`; it is disabled/rejected while `Running`.
- Selecting a message rewinds the Pod state to before that message and restores the message into the TUI composer.
- Rewind does not auto-run; pressing Enter after selection retries the restored message.
- Rewind success updates Pod session log, SegmentLogSink mirror, worker state, and TUI display consistently.
- Esc returns from the rewind picker to the normal conversation/history view without changing Pod state.
- Rewind failure leaves state unchanged and shows a clear reason.
- Picker selections are revalidated at apply time to avoid stale-head corruption.
- Rewound suffix is intentionally discarded; no fork is created.
- Tool side effects are not undone; UI/diagnostics make this clear when relevant.
- Tests cover target listing, apply success, stale-head rejection, composer restore, TUI display reseed, and at least one suffix-with-tool case.
-`cargo fmt --check`
-`cargo check -p protocol -p pod -p tui`
- Relevant focused tests.
## Out of scope
- Creating a fork when rewinding.
- Fork tree visualization.
- Merging branches.
- Undoing tool side effects.
- Rollback history stack / redo.
- Rewind across compacted segments unless it falls out naturally from implementation.
## Related
-`20260527-000009-pod-session-fork` remains a lower-priority future feature for preserving alternate histories.
`RunResult::RolledBack` should not be reused for this idle control operation. It remains the run-lifecycle signal for submit-time empty-turn rollback.
## Implementation notes
- Target identity can initially be current segment + entry index:
```rust
RewindTargetId{
segment_id: SegmentId,
user_input_entry_index: usize,
}
```
- Include `expected_head_entries` to reject stale picker selections.
- Each target should include:
- preview
- original `Vec<Segment>`
- turn/index metadata if available
- whether the target is eligible
- disabled/warning reason if relevant
- the entry count to truncate to, which is before the selected user message.
- Rewind apply must keep these in sync:
- worker history
-`user_segments`
- session store segment log
-`SegmentLogSink` mirror
- usage history / trackers
- TUI view reconstructed from returned entries
- If a complete current-state reconstruction from log is simpler and safer than maintaining many historical snapshots, prefer that over fragile partial truncation.
## Acceptance criteria
-`:rewind` opens a picker of past user messages by replacing the normal conversation/history view area, not by drawing a small popup.
-`Ctrl+R` opens the same picker only while Pod status is `Idle` or `Paused`; it is disabled/rejected while `Running`.
- Selecting a message rewinds the Pod state to before that message and restores the message into the TUI composer.
- Rewind does not auto-run; pressing Enter after selection retries the restored message.
- Rewind success updates Pod session log, SegmentLogSink mirror, worker state, and TUI display consistently.
- Esc returns from the rewind picker to the normal conversation/history view without changing Pod state.
- Rewind failure leaves state unchanged and shows a clear reason.
- Picker selections are revalidated at apply time to avoid stale-head corruption.
- Rewound suffix is intentionally discarded; no fork is created.
- Tool side effects are not undone; UI/diagnostics make this clear when relevant.
- Tests cover target listing, apply success, stale-head rejection, composer restore, TUI display reseed, and at least one suffix-with-tool case.
-`cargo fmt --check`
-`cargo check -p protocol -p pod -p tui`
- Relevant focused tests.
## Out of scope
- Creating a fork when rewinding.
- Fork tree visualization.
- Merging branches.
- Undoing tool side effects.
- Rollback history stack / redo.
- Rewind across compacted segments unless it falls out naturally from implementation.
## Related
-`20260527-000009-pod-session-fork` remains a lower-priority future feature for preserving alternate histories.
`RunResult::RolledBack` should not be reused for this idle control operation. It remains the run-lifecycle signal for submit-time empty-turn rollback.
## Implementation notes
- Target identity can initially be current segment + entry index:
```rust
RewindTargetId{
segment_id: SegmentId,
user_input_entry_index: usize,
}
```
- Include `expected_head_entries` to reject stale picker selections.
- Each target should include:
- preview
- original `Vec<Segment>`
- turn/index metadata if available
- whether the target is eligible
- disabled/warning reason if relevant
- the entry count to truncate to, which is before the selected user message.
- Rewind apply must keep these in sync:
- worker history
-`user_segments`
- session store segment log
-`SegmentLogSink` mirror
- usage history / trackers
- TUI view reconstructed from returned entries
- If a complete current-state reconstruction from log is simpler and safer than maintaining many historical snapshots, prefer that over fragile partial truncation.
## Acceptance criteria
-`:rewind` opens a picker of past user messages by replacing the normal conversation/history view area, not by drawing a small popup.
-`Ctrl+R` opens the same picker only while Pod status is `Idle` or `Paused`; it is disabled/rejected while `Running`.
- Selecting a message rewinds the Pod state to before that message and restores the message into the TUI composer.
- Rewind does not auto-run; pressing Enter after selection retries the restored message.
- Rewind success updates Pod session log, SegmentLogSink mirror, worker state, and TUI display consistently.
- Esc returns from the rewind picker to the normal conversation/history view without changing Pod state.
- Rewind failure leaves state unchanged and shows a clear reason.
- Picker selections are revalidated at apply time to avoid stale-head corruption.
- Rewound suffix is intentionally discarded; no fork is created.
- Tool side effects are not undone; UI/diagnostics make this clear when relevant.
- Tests cover target listing, apply success, stale-head rejection, composer restore, TUI display reseed, and at least one suffix-with-tool case.
-`cargo fmt --check`
-`cargo check -p protocol -p pod -p tui`
- Relevant focused tests.
## Out of scope
- Creating a fork when rewinding.
- Fork tree visualization.
- Merging branches.
- Undoing tool side effects.
- Rollback history stack / redo.
- Rewind across compacted segments unless it falls out naturally from implementation.
## Related
-`20260527-000009-pod-session-fork` remains a lower-priority future feature for preserving alternate histories.
通常 Pod の system prompt に、memory / knowledge tools の利用タイミングを短く追加する。
目的は「必要な時に過去情報を探す」ことであり、毎 turn memory query を強制することではない。memory / knowledge は helpful context だが stale になり得るため、現在の user instruction / files / tickets / git state / session log を上書きする権威として扱わせない。
## 推奨する追加文言
`resources/prompts/common/tool-usage.md` に新しい小節を足すか、`resources/prompts/common/memory.md` を作って `default.md` から include する。
例:
```md
## Memory and knowledge
Use memory and knowledge tools when the user asks about past decisions, prior requests, durable preferences, project history, or why something was done. Do not guess from vague recollection when a targeted memory lookup would answer the question.
- Use `MemoryQuery` for durable memory records: summary, decisions, and requests.
- Use `KnowledgeQuery` for project knowledge records.
- Use `MemoryRead(kind=summary)` when you need the full workspace memory summary.
- Use `MemoryRead` on returned slugs when query excerpts are insufficient.
Resident memory and knowledge are helpful context but may be stale. Current user instructions, repository files, tickets, git history, and session logs are more authoritative for exact current state.
Do not query memory on every turn. Prefer it when past context, user preferences, or prior rationale materially affects the answer or implementation.
通常 Pod の system prompt に、memory / knowledge tools の利用タイミングを短く追加する。
目的は「必要な時に過去情報を探す」ことであり、毎 turn memory query を強制することではない。memory / knowledge は helpful context だが stale になり得るため、現在の user instruction / files / tickets / git state / session log を上書きする権威として扱わせない。
## 推奨する追加文言
`resources/prompts/common/tool-usage.md` に新しい小節を足すか、`resources/prompts/common/memory.md` を作って `default.md` から include する。
例:
```md
## Memory and knowledge
Use memory and knowledge tools when the user asks about past decisions, prior requests, durable preferences, project history, or why something was done. Do not guess from vague recollection when a targeted memory lookup would answer the question.
- Use `MemoryQuery` for durable memory records: summary, decisions, and requests.
- Use `KnowledgeQuery` for project knowledge records.
- Use `MemoryRead(kind=summary)` when you need the full workspace memory summary.
- Use `MemoryRead` on returned slugs when query excerpts are insufficient.
Resident memory and knowledge are helpful context but may be stale. Current user instructions, repository files, tickets, git history, and session logs are more authoritative for exact current state.
Do not query memory on every turn. Prefer it when past context, user preferences, or prior rationale materially affects the answer or implementation.
通常 Pod の system prompt に、memory / knowledge tools の利用タイミングを短く追加する。
目的は「必要な時に過去情報を探す」ことであり、毎 turn memory query を強制することではない。memory / knowledge は helpful context だが stale になり得るため、現在の user instruction / files / tickets / git state / session log を上書きする権威として扱わせない。
## 推奨する追加文言
`resources/prompts/common/tool-usage.md` に新しい小節を足すか、`resources/prompts/common/memory.md` を作って `default.md` から include する。
例:
```md
## Memory and knowledge
Use memory and knowledge tools when the user asks about past decisions, prior requests, durable preferences, project history, or why something was done. Do not guess from vague recollection when a targeted memory lookup would answer the question.
- Use `MemoryQuery` for durable memory records: summary, decisions, and requests.
- Use `KnowledgeQuery` for project knowledge records.
- Use `MemoryRead(kind=summary)` when you need the full workspace memory summary.
- Use `MemoryRead` on returned slugs when query excerpts are insufficient.
Resident memory and knowledge are helpful context but may be stale. Current user instructions, repository files, tickets, git history, and session logs are more authoritative for exact current state.
Do not query memory on every turn. Prefer it when past context, user preferences, or prior rationale materially affects the answer or implementation.
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 -->
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# TUI: spawned child Pod の一覧と一時 attach
## 背景
insomnia の開発では、親 Pod が複数の実装 Pod / reviewer Pod を spawn し、並列に作業させる運用が増えている。現在、spawned child の状態確認や出力確認は主に tool (`ListPods`, `ReadPodOutput`, `SendToPod`, `StopPod`) 経由で行っているが、TUI 上では親 Pod の会話と child Pod の進捗を行き来しにくい。
ネイティブ GUI は将来的には便利だが、現時点で必要なタスクではない。まず TUI のまま、現在の Pod が spawn した child Pod を一覧し、一時的に attach / view できる UI を用意したい。
## Prerequisite
-`20260528-141602-tui-pod-list-view-abstraction`
This ticket should build on the shared TUI Pod list/view abstraction instead of introducing a separate child-Pod-specific list model. The child panel may specialize the source/visibility to current-parent spawned children, but row status, reachability diagnostics, attach target representation, selection, and refresh behavior should reuse the prerequisite abstraction.
## 要件
- TUI 上で、現在の Pod が spawn した child Pod を一覧できる。
- source は spawned child registry / Pod state persistence を使う。
- ホスト上の全 Pod を無条件に見せる UI にはしない。
- current parent から見える child Pod だけを対象にする。
- 各 child row には最低限以下を表示する。
- pod name
- alive / stopped / unreachable などの状態
- delegated scope の概要
- 最終更新時刻または最終出力時刻(取得できる範囲)
- 未読出力の有無または最終 assistant text preview(可能なら)
- TUI から child Pod に一時 attach / view できる。
- 親 Pod の TUI を完全に終了せず、child の履歴 / streaming 出力を確認できる。
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.
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.
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.
title:"Generalize system-reminder history append lane"
state:"closed"
created_at:"2026-05-27T00:00:20Z"
updated_at:"2026-05-29T05:05:43Z"
---
## Background
`session-todo-reminder` established the first concrete `<system-reminder>...</system-reminder>` user: Task inactivity reminders are appended through `pending_history_appends` so the reminder is persisted in `worker.history` before the next LLM request. This follows the context-processing rule that new non-volatile input must be appended to history rather than injected only into request context.
The current implementation should now be generalized so future reminder producers do not each hand-roll XML tags, `SystemItem` construction, source labeling, cooldown/priority plumbing, or history-append integration.
This ticket is about making the system-reminder append lane a small typed facility. It is not about adding new reminder policies beyond existing Task reminders.
## Requirements
- Introduce a typed internal representation for pending system reminders.
- text/body
- source/kind, e.g. task inactivity
- optional priority/order key if needed
- helper that renders the body inside `<system-reminder>...</system-reminder>` exactly once
- Route reminders through the existing `Interceptor::pending_history_appends` lane.
- The final result must still be `Item::System(SystemItem { kind: InvokeKind::SystemReminder, ... })` or equivalent current protocol type.
- The reminder must be appended to `worker.history`; do not introduce hidden request-only context injection.
- Refactor `session-todo-reminder` to use this typed helper/facility.
- Task reminder behavior, thresholds, cooldown, and tests should remain unchanged.
- The helper should prevent double-wrapping if the body is already tagged, or the API should make double-wrapping impossible.
- Keep `Notify` / `PodEvent` behavior unchanged.
- Do not merge raw notify and system reminder semantics.
- If they share buffering mechanics, keep the public behavior and rendered tags distinct.
- Keep ordering deterministic.
- If multiple reminder producers are added later, ordering should be explicit or stable.
- For now, existing Task reminder order relative to Notify/PodEvent should be preserved unless there is a clear reason to change it.
- Add docs/comments near the facility explaining the rule:
- system reminders are durable input and must be appended through history.
- they are not transient UI notices.
- they are not prompt-cache/context-only injections.
## Acceptance criteria
- There is a typed system-reminder helper/facility rather than ad-hoc string construction in Task reminder code.
- Task inactivity reminders still appear as `<system-reminder>...</system-reminder>` in `pending_history_appends` output.
- The helper emits `InvokeKind::SystemReminder` / current system-reminder item kind.
- Existing Task reminder tests continue to pass.
- New focused tests cover:
- rendering wraps body once.
- source/kind is retained or observable where appropriate.
- Task reminder uses the helper and remains history-append based.
- no hidden context-only injection path is introduced.
-`cargo fmt --check`
-`cargo check -p pod -p llm-worker -p session-store`
- Relevant focused tests, e.g. `cargo test -p pod reminder --no-default-features`.
## Out of scope
- Adding a second reminder policy.
- Changing Task reminder thresholds/cooldown.
- Changing Notify/PodEvent user-visible behavior.
- UI actionbar notices.
- Prompt text changes.
- Generic notification center or reminder scheduling service.
`session-todo-reminder` established the first concrete `<system-reminder>...</system-reminder>` user: Task inactivity reminders are appended through `pending_history_appends` so the reminder is persisted in `worker.history` before the next LLM request. This follows the context-processing rule that new non-volatile input must be appended to history rather than injected only into request context.
The current implementation should now be generalized so future reminder producers do not each hand-roll XML tags, `SystemItem` construction, source labeling, cooldown/priority plumbing, or history-append integration.
This ticket is about making the system-reminder append lane a small typed facility. It is not about adding new reminder policies beyond existing Task reminders.
## Requirements
- Introduce a typed internal representation for pending system reminders.
- text/body
- source/kind, e.g. task inactivity
- optional priority/order key if needed
- helper that renders the body inside `<system-reminder>...</system-reminder>` exactly once
- Route reminders through the existing `Interceptor::pending_history_appends` lane.
- The final result must still be `Item::System(SystemItem { kind: InvokeKind::SystemReminder, ... })` or equivalent current protocol type.
- The reminder must be appended to `worker.history`; do not introduce hidden request-only context injection.
- Refactor `session-todo-reminder` to use this typed helper/facility.
- Task reminder behavior, thresholds, cooldown, and tests should remain unchanged.
- The helper should prevent double-wrapping if the body is already tagged, or the API should make double-wrapping impossible.
- Keep `Notify` / `PodEvent` behavior unchanged.
- Do not merge raw notify and system reminder semantics.
- If they share buffering mechanics, keep the public behavior and rendered tags distinct.
- Keep ordering deterministic.
- If multiple reminder producers are added later, ordering should be explicit or stable.
- For now, existing Task reminder order relative to Notify/PodEvent should be preserved unless there is a clear reason to change it.
- Add docs/comments near the facility explaining the rule:
- system reminders are durable input and must be appended through history.
- they are not transient UI notices.
- they are not prompt-cache/context-only injections.
## Acceptance criteria
- There is a typed system-reminder helper/facility rather than ad-hoc string construction in Task reminder code.
- Task inactivity reminders still appear as `<system-reminder>...</system-reminder>` in `pending_history_appends` output.
- The helper emits `InvokeKind::SystemReminder` / current system-reminder item kind.
- Existing Task reminder tests continue to pass.
- New focused tests cover:
- rendering wraps body once.
- source/kind is retained or observable where appropriate.
- Task reminder uses the helper and remains history-append based.
- no hidden context-only injection path is introduced.
-`cargo fmt --check`
-`cargo check -p pod -p llm-worker -p session-store`
- Relevant focused tests, e.g. `cargo test -p pod reminder --no-default-features`.
## Out of scope
- Adding a second reminder policy.
- Changing Task reminder thresholds/cooldown.
- Changing Notify/PodEvent user-visible behavior.
- UI actionbar notices.
- Prompt text changes.
- Generic notification center or reminder scheduling service.
`session-todo-reminder` established the first concrete `<system-reminder>...</system-reminder>` user: Task inactivity reminders are appended through `pending_history_appends` so the reminder is persisted in `worker.history` before the next LLM request. This follows the context-processing rule that new non-volatile input must be appended to history rather than injected only into request context.
The current implementation should now be generalized so future reminder producers do not each hand-roll XML tags, `SystemItem` construction, source labeling, cooldown/priority plumbing, or history-append integration.
This ticket is about making the system-reminder append lane a small typed facility. It is not about adding new reminder policies beyond existing Task reminders.
## Requirements
- Introduce a typed internal representation for pending system reminders.
- text/body
- source/kind, e.g. task inactivity
- optional priority/order key if needed
- helper that renders the body inside `<system-reminder>...</system-reminder>` exactly once
- Route reminders through the existing `Interceptor::pending_history_appends` lane.
- The final result must still be `Item::System(SystemItem { kind: InvokeKind::SystemReminder, ... })` or equivalent current protocol type.
- The reminder must be appended to `worker.history`; do not introduce hidden request-only context injection.
- Refactor `session-todo-reminder` to use this typed helper/facility.
- Task reminder behavior, thresholds, cooldown, and tests should remain unchanged.
- The helper should prevent double-wrapping if the body is already tagged, or the API should make double-wrapping impossible.
- Keep `Notify` / `PodEvent` behavior unchanged.
- Do not merge raw notify and system reminder semantics.
- If they share buffering mechanics, keep the public behavior and rendered tags distinct.
- Keep ordering deterministic.
- If multiple reminder producers are added later, ordering should be explicit or stable.
- For now, existing Task reminder order relative to Notify/PodEvent should be preserved unless there is a clear reason to change it.
- Add docs/comments near the facility explaining the rule:
- system reminders are durable input and must be appended through history.
- they are not transient UI notices.
- they are not prompt-cache/context-only injections.
## Acceptance criteria
- There is a typed system-reminder helper/facility rather than ad-hoc string construction in Task reminder code.
- Task inactivity reminders still appear as `<system-reminder>...</system-reminder>` in `pending_history_appends` output.
- The helper emits `InvokeKind::SystemReminder` / current system-reminder item kind.
- Existing Task reminder tests continue to pass.
- New focused tests cover:
- rendering wraps body once.
- source/kind is retained or observable where appropriate.
- Task reminder uses the helper and remains history-append based.
- no hidden context-only injection path is introduced.
-`cargo fmt --check`
-`cargo check -p pod -p llm-worker -p session-store`
- Relevant focused tests, e.g. `cargo test -p pod reminder --no-default-features`.
## Out of scope
- Adding a second reminder policy.
- Changing Task reminder thresholds/cooldown.
- Changing Notify/PodEvent user-visible behavior.
- UI actionbar notices.
- Prompt text changes.
- Generic notification center or reminder scheduling service.
Closed without implementation for now. Current Bash tool description already nudges agents toward Read/Edit/Glob/Grep over shell-based file edits, and this is not urgent enough to carry as an active work item. If the behavior becomes a recurring problem, reopen as a focused prompt-description polish ticket covering Bash child processes such as cat/tee/sed/perl/python rewrites.
Migrated from TODO.md entry without a legacy ticket file. No legacy review file was present at migration time.
---
<!-- event: close author: hare at: 2026-05-31T22:36:34Z status: closed -->
## Closed
Closed without implementation for now. Current Bash tool description already nudges agents toward Read/Edit/Glob/Grep over shell-based file edits, and this is not urgent enough to carry as an active work item. If the behavior becomes a recurring problem, reopen as a focused prompt-description polish ticket covering Bash child processes such as cat/tee/sed/perl/python rewrites.
The current manifest cascade is good at configuration defaults by location: built-in defaults, user manifest, workspace manifest, and explicit overlays. That is less suitable for operational role selection. Users want to choose between profiles such as Orchestrator, Coder, Researcher, Reviewer, or cheap/fast variants, and they want those profiles to be portable as a pure artifact rather than assembled implicitly from several ambient layers.
Another problem is authoring ergonomics. The current manifest exposes many low-level numeric parameters that require implementation-specific intuition, such as compaction thresholds, pruning protection sizes, memory thresholds, and feature-specific token limits. Profiles should let users express high-level intent and reusable presets while the resolver produces the precise runtime manifest.
## Related work
-`work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md`: profiles should integrate with explicit encrypted secret references so API keys/tokens are not limited to process environment variables.
## Design direction
Use Nix as the default human-authored profile format. A profile is a Nix expression that produces the final Pod manifest/configuration artifact through an Insomnia-provided `mkProfile` / `mkManifest` style library.
The profile itself is the source of truth. Commonality, imports, role presets, and any cascade-like behavior should be expressed in Nix by the profile author instead of being implemented as an additional ambient manifest cascade in Insomnia.
Do not introduce a three-layer authoring model where Nix generates TOML profiles that then merge into TOML manifests. That would make manifest/profile/Nix ownership unclear and hard to operate. Rust should consume the resolved artifact, ideally as a typed JSON/config representation, and preserve a snapshot for Pod restore.
## Requirements
- Add a Nix-based profile entrypoint as the default path for new Pod creation.
- Provide an Insomnia Nix library with `mkProfile` / `mkManifest` helpers.
- The helper should produce a pure resolved manifest/config artifact that Rust can deserialize and validate.
- Profile authors may use Nix imports/functions to share common settings, implement their own cascade, or build role presets.
- Treat the resolved manifest/config as the runtime contract.
- Persist the selected profile identity/source and the resolved snapshot in Pod/session metadata.
- Pod resume should prefer the saved resolved snapshot, not silently re-evaluate the Nix profile.
- Re-evaluating a profile for an existing Pod must be explicit because it may change model, tools, permissions, or thresholds.
- Move role-oriented authoring into profiles.
- Support profiles for roles such as Orchestrator, Coder, Researcher, Reviewer, and cost/performance variants.
- Profiles should be able to select model/provider settings, prompts, tools, permissions, memory behavior, web/search behavior, workflows, skills, and context/compaction strategy.
- Prefer semantic presets in the Nix library for values that are difficult to tune by raw numbers, e.g. context budget, compaction behavior, retention, autonomy, and tool policy.
- Keep raw low-level numeric overrides available as an advanced escape hatch, not the primary user-facing interface.
- Shrink ambient cascade to discovery/default selection rather than runtime config merging.
- User/project configuration may provide profile registries, aliases, defaults, and UI preferences.
- User/project configuration should not be required as intermediate runtime override layers for model IDs, compaction thresholds, or other behavior controlled by the selected profile.
- Existing TOML manifest cascade can remain as compatibility/debug/test infrastructure, but it should not be the main profile design.
- Add profile discovery and selection UX.
- New Pod creation UI should show a selectable profile field such as `profile: coder (default)`.
- The profile picker should list built-in/user/project/explicit profiles with enough source/default information to avoid ambiguity.
- CLI/TUI should support explicit profile selection by name/source and by path/flakeref where appropriate.
- Ambiguous profile names should fail closed or require source-qualified selection rather than being implicitly merged.
- Keep secrets as references, not plaintext values.
- Nix profiles may refer to credentials using typed secret references, e.g. `secrets.ref "brave.search.default"`.
- Nix evaluation output, resolved config serialization, diagnostics, session logs, and model context must not contain plaintext secrets.
- Secret dereferencing/decryption happens in Rust at the consumer boundary.
- Define compatibility and fallback behavior.
-`--manifest` / TOML manifest loading may continue to work for compatibility, tests, fixtures, and low-level debugging.
- If Nix is unavailable, diagnostics should clearly say that profile resolution requires Nix and point to the manifest/resolved-config fallback path.
- Existing manifest behavior should not be broken until the Nix profile path is implemented and documented.
## Open design points
- Exact Nix entrypoint shape:
- flake output names, e.g. `insomniaProfiles.<name>` / `profiles.<name>`
- path-based profiles, e.g. `.insomnia/profiles/coder/profile.nix`
- whether both are supported initially
- Exact Rust-facing artifact:
- JSON resolved config vs TOML manifest snapshot vs a new typed `ResolvedPodConfig`
- whether `PodManifest` remains the final runtime type or becomes the legacy/compatibility representation
- Profile registry/default storage:
- where user-level profile aliases live
- where project-level defaults live
- how built-in profiles are exposed
- How much Nix support is external-command based initially vs embedded/library-integrated later.
- How profile summaries are generated for the new Pod UI without exposing low-level internals or secrets.
## Acceptance criteria
- A Nix profile can be selected when creating a new Pod and resolves to the complete runtime manifest/config for that Pod.
- Insomnia provides a documented `mkProfile` / `mkManifest` Nix helper for producing a valid resolved profile artifact.
- Profile authors can share common settings and implement cascade-like composition in Nix without relying on ambient user/project manifest merging.
- New Pod UI includes profile selection and displays the effective default, e.g. `profile: coder (default)`.
- CLI/TUI profile selection supports at least one explicit path/flakeref flow and one discovered-name/default flow.
- Resolved profile artifacts are validated with clear diagnostics before Pod creation.
- Pod/session metadata persists the selected profile identity/source and the resolved snapshot.
- Pod resume uses the persisted resolved snapshot unless the user explicitly asks to reload/re-resolve the profile.
- Secret references are preserved as references through Nix evaluation and resolved config; plaintext secrets are not written to config snapshots, logs, diagnostics, or model context.
- Existing TOML manifest path remains available as a compatibility/debug/test path during the migration.
- Documentation explains the new profile model, why ambient cascade is no longer the primary runtime config mechanism, and how users should structure reusable Nix profiles.
The current manifest cascade is good at configuration defaults by location: built-in defaults, user manifest, workspace manifest, and explicit overlays. That is less suitable for operational role selection. Users want to choose between profiles such as Orchestrator, Coder, Researcher, Reviewer, or cheap/fast variants, and they want those profiles to be portable as a pure artifact rather than assembled implicitly from several ambient layers.
Another problem is authoring ergonomics. The current manifest exposes many low-level numeric parameters that require implementation-specific intuition, such as compaction thresholds, pruning protection sizes, memory thresholds, and feature-specific token limits. Profiles should let users express high-level intent and reusable presets while the resolver produces the precise runtime manifest.
## Related work
-`work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md`: profiles should integrate with explicit encrypted secret references so API keys/tokens are not limited to process environment variables.
## Design direction
Use Nix as the default human-authored profile format. A profile is a Nix expression that produces the final Pod manifest/configuration artifact through an Insomnia-provided `mkProfile` / `mkManifest` style library.
The profile itself is the source of truth. Commonality, imports, role presets, and any cascade-like behavior should be expressed in Nix by the profile author instead of being implemented as an additional ambient manifest cascade in Insomnia.
Do not introduce a three-layer authoring model where Nix generates TOML profiles that then merge into TOML manifests. That would make manifest/profile/Nix ownership unclear and hard to operate. Rust should consume the resolved artifact, ideally as a typed JSON/config representation, and preserve a snapshot for Pod restore.
## Requirements
- Add a Nix-based profile entrypoint as the default path for new Pod creation.
- Provide an Insomnia Nix library with `mkProfile` / `mkManifest` helpers.
- The helper should produce a pure resolved manifest/config artifact that Rust can deserialize and validate.
- Profile authors may use Nix imports/functions to share common settings, implement their own cascade, or build role presets.
- Treat the resolved manifest/config as the runtime contract.
- Persist the selected profile identity/source and the resolved snapshot in Pod/session metadata.
- Pod resume should prefer the saved resolved snapshot, not silently re-evaluate the Nix profile.
- Re-evaluating a profile for an existing Pod must be explicit because it may change model, tools, permissions, or thresholds.
- Move role-oriented authoring into profiles.
- Support profiles for roles such as Orchestrator, Coder, Researcher, Reviewer, and cost/performance variants.
- Profiles should be able to select model/provider settings, prompts, tools, permissions, memory behavior, web/search behavior, workflows, skills, and context/compaction strategy.
- Prefer semantic presets in the Nix library for values that are difficult to tune by raw numbers, e.g. context budget, compaction behavior, retention, autonomy, and tool policy.
- Keep raw low-level numeric overrides available as an advanced escape hatch, not the primary user-facing interface.
- Shrink ambient cascade to discovery/default selection rather than runtime config merging.
- User/project configuration may provide profile registries, aliases, defaults, and UI preferences.
- User/project configuration should not be required as intermediate runtime override layers for model IDs, compaction thresholds, or other behavior controlled by the selected profile.
- Existing TOML manifest cascade can remain as compatibility/debug/test infrastructure, but it should not be the main profile design.
- Add profile discovery and selection UX.
- New Pod creation UI should show a selectable profile field such as `profile: coder (default)`.
- The profile picker should list built-in/user/project/explicit profiles with enough source/default information to avoid ambiguity.
- CLI/TUI should support explicit profile selection by name/source and by path/flakeref where appropriate.
- Ambiguous profile names should fail closed or require source-qualified selection rather than being implicitly merged.
- Keep secrets as references, not plaintext values.
- Nix profiles may refer to credentials using typed secret references, e.g. `secrets.ref "brave.search.default"`.
- Nix evaluation output, resolved config serialization, diagnostics, session logs, and model context must not contain plaintext secrets.
- Secret dereferencing/decryption happens in Rust at the consumer boundary.
- Define compatibility and fallback behavior.
-`--manifest` / TOML manifest loading may continue to work for compatibility, tests, fixtures, and low-level debugging.
- If Nix is unavailable, diagnostics should clearly say that profile resolution requires Nix and point to the manifest/resolved-config fallback path.
- Existing manifest behavior should not be broken until the Nix profile path is implemented and documented.
## Open design points
- Exact Nix entrypoint shape:
- flake output names, e.g. `insomniaProfiles.<name>` / `profiles.<name>`
- path-based profiles, e.g. `.insomnia/profiles/coder/profile.nix`
- whether both are supported initially
- Exact Rust-facing artifact:
- JSON resolved config vs TOML manifest snapshot vs a new typed `ResolvedPodConfig`
- whether `PodManifest` remains the final runtime type or becomes the legacy/compatibility representation
- Profile registry/default storage:
- where user-level profile aliases live
- where project-level defaults live
- how built-in profiles are exposed
- How much Nix support is external-command based initially vs embedded/library-integrated later.
- How profile summaries are generated for the new Pod UI without exposing low-level internals or secrets.
## Acceptance criteria
- A Nix profile can be selected when creating a new Pod and resolves to the complete runtime manifest/config for that Pod.
- Insomnia provides a documented `mkProfile` / `mkManifest` Nix helper for producing a valid resolved profile artifact.
- Profile authors can share common settings and implement cascade-like composition in Nix without relying on ambient user/project manifest merging.
- New Pod UI includes profile selection and displays the effective default, e.g. `profile: coder (default)`.
- CLI/TUI profile selection supports at least one explicit path/flakeref flow and one discovered-name/default flow.
- Resolved profile artifacts are validated with clear diagnostics before Pod creation.
- Pod/session metadata persists the selected profile identity/source and the resolved snapshot.
- Pod resume uses the persisted resolved snapshot unless the user explicitly asks to reload/re-resolve the profile.
- Secret references are preserved as references through Nix evaluation and resolved config; plaintext secrets are not written to config snapshots, logs, diagnostics, or model context.
- Existing TOML manifest path remains available as a compatibility/debug/test path during the migration.
- Documentation explains the new profile model, why ambient cascade is no longer the primary runtime config mechanism, and how users should structure reusable Nix profiles.
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-29T16:09:27Z -->
## Plan
Implementation will proceed through a child orchestrator Pod in a dedicated worktree as an experiment in nested Pod delegation.
Initial implementation target:
- Introduce Nix profile resolution as a new manifest source before the existing manifest cascade.
- Start with explicit path-based profiles; discovered-name/default selection and rich TUI picker can be staged after the core resolver if necessary.
- Provide a minimal bundled Nix helper that can produce a typed resolved manifest/config artifact.
- Keep existing TOML manifest loading as compatibility/debug/test infrastructure.
- Persist enough profile identity and resolved snapshot data for future restore semantics; do not silently re-evaluate profiles on resume.
- Secret values must remain references only; plaintext secrets are out of scope for the profile resolver.
The child orchestrator may split implementation among sub-Pods, but final merge/close remains parent-side.
---
<!-- event: review author: hare at: 2026-05-29T16:52:47Z status: approve -->
## Review: approve
Reviewed the nested Pod implementation from branch `work/nix-manifest-profiles`.
Result: approved after blocking fix.
Findings:
- Initial review found one blocking issue: `--profile` rejected non-empty `INSOMNIA_USER_MANIFEST`. That contradicted the profile design because a selected Nix profile is a standalone runtime config artifact and ambient user/project manifest cascade should not affect it.
- The child orchestrator fixed this in commit `c9a175a fix: ignore user manifest for profiles` and added `profile_ignores_non_empty_user_manifest_env` without invoking real Nix.
- Profile foundation is intentionally a vertical slice: explicit path-based Nix profile resolution, minimal Nix helper, CLI/TUI spawn entrypoints, resolved snapshot metadata, and restore-from-snapshot behavior. Rich discovery/default picker remains future work.
Validation run by reviewer:
-`cargo fmt --check`
-`cargo test -p manifest profile -- --nocapture`
-`cargo test -p pod --bin insomnia-pod profile -- --nocapture`
- Manual `nix eval --json --file` smoke check for `resources/nix/profile-lib.nix`
Non-blocking follow-up candidates:
- Hide or narrow `ResolvedProfile::raw_artifact` if future call sites might log/persist accidental raw Nix output.
- Add a timeout around `nix eval` so profile startup cannot hang indefinitely.
- Validate direct `client::SpawnConfig` construction that combines `profile_path` with `resume_from`; TUI currently avoids it.
- Build richer profile discovery/default selection and the full TUI profile picker.
---
<!-- event: plan author: hare at: 2026-05-29T16:59:41Z -->
## Plan
Continue implementation beyond the merged Nix profile foundation. The previous nested delegation stopped at a reviewable vertical slice; this phase should keep iterating until the work item acceptance criteria are materially closer to completion or a real blocker is found.
Phase 2 target:
- Add profile discovery/default selection instead of requiring only explicit `--profile <path>`.
- Support discovered-name selection with source disambiguation where needed, e.g. builtin/user/project/path semantics.
- Add or substantially advance the new Pod profile selection UX so a default can be displayed as `profile: coder (default)` and changed before spawn.
- Preserve the core design: selected Nix profile resolves to a standalone manifest/config artifact; ambient user/project manifests do not merge into it.
- Keep Pod resume using resolved snapshots rather than silent Nix re-evaluation.
- Keep secrets as references only.
The delegated orchestrator should not return merely because one slice is reviewable. It should internally review acceptance criteria, delegate sub-Pods as needed, and continue with the next reachable slice unless it hits a concrete design or technical blocker. Parent-side merge/close remains reserved for this parent Pod.
---
<!-- event: review author: hare at: 2026-05-29T17:44:47Z status: approve -->
## Review: approve
Reviewed phase 2 implementation from branch `work/nix-manifest-profiles-phase2`.
Result: approved after two parent-review fixes.
Implemented scope:
- Profile registry/discovery for builtin/user/project sources.
-`[profiles]` metadata in user/project manifests for discovery/default/alias only; it is not merged into the selected runtime manifest.
-`--profile` selector parsing for explicit paths, `path:<path>`, discovered names, `default`, and source-qualified names such as `project:coder`.
- TUI fresh-spawn UI now shows a selectable `profile:` row, uses discovered choices, marks defaults, and includes `manifest cascade` as opt-out.
- SpawnConfig passes selected profiles to `insomnia-pod --profile`; resume/attach paths do not re-evaluate profiles.
- Docs and focused tests updated.
Parent review findings fixed by child orchestrator:
1. Unqualified alias targets initially resolved globally. Fixed so aliases declared in a source resolve unqualified targets within that declaring source by default.
2. Defaults pointing at aliases initially did not mark the resolved target entry as default, causing TUI to fall back to `manifest cascade`. Fixed by resolving the default through `select_named()` before setting `is_default` flags.
Validation run by parent reviewer:
-`cargo fmt --check`
-`cargo check`
-`cargo test -p manifest profile -- --nocapture`
-`cargo test -p tui spawn -- --nocapture`
-`cargo test -p pod profile -- --nocapture`
-`cargo test -p client spawn -- --nocapture`
-`git diff --check`
All passed. Full `cargo test` was run by the child orchestrator and failed only in the unrelated existing/flaky `llm-worker` parallel timing test class.
Remaining polish/follow-up candidates, not blockers for this work item:
- A richer popup-style profile picker instead of inline cycling.
- Actual bundled builtin profile files once default builtin semantics are decided.
-`nix eval` timeout/robustness follow-up.
- Encrypted secret store integration remains tracked by the related encrypted-secrets work item.
---
<!-- event: close author: hare at: 2026-05-29T17:45:59Z status: closed -->
## Closed
---
id: 20260527-000022-manifest-profiles
slug: manifest-profiles
title: Nix profile entrypoints that resolve to portable Pod manifests
status: closed
kind: feature
priority: P2
labels: [manifest, profiles, nix, tui]
created_at: 2026-05-27T00:00:22Z
updated_at: 2026-05-29T17:45:59Z
assignee: null
legacy_ticket: null
---
## Migration reference
- legacy_ticket: null
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# Nix profile entrypoints that resolve to portable Pod manifests
## Background
This work item was migrated from an unfinished TODO.md entry:
The current manifest cascade is good at configuration defaults by location: built-in defaults, user manifest, workspace manifest, and explicit overlays. That is less suitable for operational role selection. Users want to choose between profiles such as Orchestrator, Coder, Researcher, Reviewer, or cheap/fast variants, and they want those profiles to be portable as a pure artifact rather than assembled implicitly from several ambient layers.
Another problem is authoring ergonomics. The current manifest exposes many low-level numeric parameters that require implementation-specific intuition, such as compaction thresholds, pruning protection sizes, memory thresholds, and feature-specific token limits. Profiles should let users express high-level intent and reusable presets while the resolver produces the precise runtime manifest.
## Related work
-`work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md`: profiles should integrate with explicit encrypted secret references so API keys/tokens are not limited to process environment variables.
## Design direction
Use Nix as the default human-authored profile format. A profile is a Nix expression that produces the final Pod manifest/configuration artifact through an Insomnia-provided `mkProfile` / `mkManifest` style library.
The profile itself is the source of truth. Commonality, imports, role presets, and any cascade-like behavior should be expressed in Nix by the profile author instead of being implemented as an additional ambient manifest cascade in Insomnia.
Do not introduce a three-layer authoring model where Nix generates TOML profiles that then merge into TOML manifests. That would make manifest/profile/Nix ownership unclear and hard to operate. Rust should consume the resolved artifact, ideally as a typed JSON/config representation, and preserve a snapshot for Pod restore.
## Requirements
- Add a Nix-based profile entrypoint as the default path for new Pod creation.
- Provide an Insomnia Nix library with `mkProfile` / `mkManifest` helpers.
- The helper should produce a pure resolved manifest/config artifact that Rust can deserialize and validate.
- Profile authors may use Nix imports/functions to share common settings, implement their own cascade, or build role presets.
- Treat the resolved manifest/config as the runtime contract.
- Persist the selected profile identity/source and the resolved snapshot in Pod/session metadata.
- Pod resume should prefer the saved resolved snapshot, not silently re-evaluate the Nix profile.
- Re-evaluating a profile for an existing Pod must be explicit because it may change model, tools, permissions, or thresholds.
- Move role-oriented authoring into profiles.
- Support profiles for roles such as Orchestrator, Coder, Researcher, Reviewer, and cost/performance variants.
- Profiles should be able to select model/provider settings, prompts, tools, permissions, memory behavior, web/search behavior, workflows, skills, and context/compaction strategy.
- Prefer semantic presets in the Nix library for values that are difficult to tune by raw numbers, e.g. context budget, compaction behavior, retention, autonomy, and tool policy.
- Keep raw low-level numeric overrides available as an advanced escape hatch, not the primary user-facing interface.
- Shrink ambient cascade to discovery/default selection rather than runtime config merging.
- User/project configuration may provide profile registries, aliases, defaults, and UI preferences.
- User/project configuration should not be required as intermediate runtime override layers for model IDs, compaction thresholds, or other behavior controlled by the selected profile.
- Existing TOML manifest cascade can remain as compatibility/debug/test infrastructure, but it should not be the main profile design.
- Add profile discovery and selection UX.
- New Pod creation UI should show a selectable profile field such as `profile: coder (default)`.
- The profile picker should list built-in/user/project/explicit profiles with enough source/default information to avoid ambiguity.
- CLI/TUI should support explicit profile selection by name/source and by path/flakeref where appropriate.
- Ambiguous profile names should fail closed or require source-qualified selection rather than being implicitly merged.
- Keep secrets as references, not plaintext values.
- Nix profiles may refer to credentials using typed secret references, e.g. `secrets.ref "brave.search.default"`.
- Nix evaluation output, resolved config serialization, diagnostics, session logs, and model context must not contain plaintext secrets.
- Secret dereferencing/decryption happens in Rust at the consumer boundary.
- Define compatibility and fallback behavior.
-`--manifest` / TOML manifest loading may continue to work for compatibility, tests, fixtures, and low-level debugging.
- If Nix is unavailable, diagnostics should clearly say that profile resolution requires Nix and point to the manifest/resolved-config fallback path.
- Existing manifest behavior should not be broken until the Nix profile path is implemented and documented.
## Open design points
- Exact Nix entrypoint shape:
- flake output names, e.g. `insomniaProfiles.<name>` / `profiles.<name>`
- path-based profiles, e.g. `.insomnia/profiles/coder/profile.nix`
- whether both are supported initially
- Exact Rust-facing artifact:
- JSON resolved config vs TOML manifest snapshot vs a new typed `ResolvedPodConfig`
- whether `PodManifest` remains the final runtime type or becomes the legacy/compatibility representation
- Profile registry/default storage:
- where user-level profile aliases live
- where project-level defaults live
- how built-in profiles are exposed
- How much Nix support is external-command based initially vs embedded/library-integrated later.
- How profile summaries are generated for the new Pod UI without exposing low-level internals or secrets.
## Acceptance criteria
- A Nix profile can be selected when creating a new Pod and resolves to the complete runtime manifest/config for that Pod.
- Insomnia provides a documented `mkProfile` / `mkManifest` Nix helper for producing a valid resolved profile artifact.
- Profile authors can share common settings and implement cascade-like composition in Nix without relying on ambient user/project manifest merging.
- New Pod UI includes profile selection and displays the effective default, e.g. `profile: coder (default)`.
- CLI/TUI profile selection supports at least one explicit path/flakeref flow and one discovered-name/default flow.
- Resolved profile artifacts are validated with clear diagnostics before Pod creation.
- Pod/session metadata persists the selected profile identity/source and the resolved snapshot.
- Pod resume uses the persisted resolved snapshot unless the user explicitly asks to reload/re-resolve the profile.
- Secret references are preserved as references through Nix evaluation and resolved config; plaintext secrets are not written to config snapshots, logs, diagnostics, or model context.
- Existing TOML manifest path remains available as a compatibility/debug/test path during the migration.
- Documentation explains the new profile model, why ambient cascade is no longer the primary runtime config mechanism, and how users should structure reusable Nix profiles.
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.