Compare commits

..

9 Commits

18 changed files with 794 additions and 58 deletions

View File

@ -507,6 +507,18 @@ mod tests {
assert!(rendered.contains("mark_read_required"));
}
#[test]
fn internal_worker_prompts_do_not_include_default_memory_guidance() {
let cat = PromptCatalog::builtins_only().unwrap();
let compact = cat.compact_system().unwrap();
let extract = cat.memory_extract_system("Japanese").unwrap();
let consolidate = cat.memory_consolidation_system("Japanese").unwrap();
for rendered in [compact, extract, consolidate] {
assert!(!rendered.contains("### Memory and knowledge"));
assert!(!rendered.contains("Do not query memory every turn"));
}
}
#[test]
fn memory_worker_prompts_include_language() {
let cat = PromptCatalog::builtins_only().unwrap();

View File

@ -440,7 +440,11 @@ mod tests {
let rendered = tmpl
.render(&ctx(dir.path(), &scope, vec!["Read".into()], None))
.unwrap();
// Builtin default body must expose the language policy.
// Builtin default body must expose the tool and language policies.
assert!(rendered.contains("### Memory and knowledge"));
assert!(rendered.contains("MemoryQuery"));
assert!(rendered.contains("MemoryRead(kind=summary)"));
assert!(rendered.contains("Do not query memory every turn"));
assert!(rendered.contains("## Language"));
assert!(rendered.contains("`language`: `match the user's language"));
// Trailing section must be present.

View File

@ -44,6 +44,8 @@ use crate::app::App;
use crate::picker::PickerOutcome;
use crate::spawn::{SpawnOutcome, SpawnReady};
type FullscreenTerminal = Terminal<CrosstermBackend<io::Stdout>>;
fn resolve_socket(pod_name: &str, override_path: Option<PathBuf>) -> PathBuf {
if let Some(p) = override_path {
return p;
@ -289,33 +291,95 @@ async fn run_pod_name(
pod_name: String,
socket_override: Option<PathBuf>,
) -> Result<(), Box<dyn std::error::Error>> {
let preferred_socket = resolve_socket(&pod_name, socket_override.clone());
if let Some((_socket_path, client)) =
connect_live_pod(&pod_name, preferred_socket, socket_override.is_none()).await
{
if let Some(client) = try_connect_live_pod(&pod_name, socket_override.clone()).await {
let mut terminal = enter_fullscreen()?;
let mut app = App::new(pod_name);
app.connected = true;
return run_loop(&mut terminal, &mut app, client).await;
run_connected_pod(&mut terminal, pod_name, client).await?;
return Ok(());
}
let ready = match spawn::run_pod_name(pod_name).await? {
SpawnOutcome::Ready(r) => r,
SpawnOutcome::Cancelled => return Ok(()),
};
let mut terminal = enter_fullscreen()?;
terminal.clear()?;
let result = run_ready_pod(&mut terminal, ready).await;
let _ = leave_fullscreen(&mut terminal);
result
}
async fn run_connected_pod(
terminal: &mut FullscreenTerminal,
pod_name: String,
client: PodClient,
) -> Result<(), Box<dyn std::error::Error>> {
let mut app = App::new(pod_name);
app.connected = true;
run_loop(terminal, &mut app, client).await
}
async fn run_pod_name_nested(
terminal: &mut FullscreenTerminal,
request: multi_pod::OpenPodRequest,
) -> Result<(), Box<dyn std::error::Error>> {
let multi_pod::OpenPodRequest {
pod_name,
socket_override,
} = request;
if let Some(client) = try_connect_live_pod(&pod_name, socket_override).await {
return run_connected_pod(terminal, pod_name, client).await;
}
let ready = spawn_pod_name_from_fullscreen(terminal, &pod_name).await?;
run_ready_pod(terminal, ready).await
}
async fn spawn_pod_name_from_fullscreen(
terminal: &mut FullscreenTerminal,
pod_name: &str,
) -> Result<SpawnReady, Box<dyn std::error::Error>> {
leave_fullscreen(terminal)?;
let outcome = spawn::run_pod_name(pod_name.to_string()).await;
enter_fullscreen_existing(terminal)?;
terminal.clear()?;
match outcome? {
SpawnOutcome::Ready(ready) => Ok(ready),
SpawnOutcome::Cancelled => Err(Box::new(NestedOpenCancelled)),
}
}
async fn try_connect_live_pod(
pod_name: &str,
socket_override: Option<PathBuf>,
) -> Option<PodClient> {
let preferred_socket = resolve_socket(pod_name, socket_override.clone());
connect_live_pod(pod_name, preferred_socket, socket_override.is_none())
.await
.map(|(_, client)| client)
}
#[derive(Debug)]
struct NestedOpenCancelled;
impl std::fmt::Display for NestedOpenCancelled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Pod open was cancelled")
}
}
impl std::error::Error for NestedOpenCancelled {}
async fn run_ready_pod(
terminal: &mut FullscreenTerminal,
ready: SpawnReady,
) -> Result<(), Box<dyn std::error::Error>> {
let SpawnReady {
pod_name,
socket_path,
} = ready;
let mut terminal = enter_fullscreen()?;
let result = run(&mut terminal, pod_name, &socket_path).await;
let _ = execute!(
terminal.backend_mut(),
DisableMouseCapture,
LeaveAlternateScreen
);
result
run(terminal, pod_name, &socket_path).await
}
async fn connect_live_pod(
@ -354,24 +418,37 @@ async fn run_resume() -> Result<(), Box<dyn std::error::Error>> {
}
async fn run_multi() -> Result<(), Box<dyn std::error::Error>> {
let mut app = multi_pod::load_app().await?;
let mut terminal = enter_fullscreen()?;
let outcome = multi_pod::run(&mut terminal).await;
let _ = execute!(
terminal.backend_mut(),
DisableMouseCapture,
LeaveAlternateScreen
);
match outcome? {
multi_pod::MultiPodOutcome::Quit => Ok(()),
multi_pod::MultiPodOutcome::Open {
pod_name,
socket_override,
} => run_pod_name(pod_name, socket_override).await,
loop {
match multi_pod::run(&mut terminal, &mut app).await? {
multi_pod::MultiPodOutcome::Quit => {
let _ = leave_fullscreen(&mut terminal);
return Ok(());
}
multi_pod::MultiPodOutcome::Open(request) => {
let pod_name = request.pod_name.clone();
match run_pod_name_nested(&mut terminal, request).await {
Ok(()) => app.finish_open(&pod_name, Ok(())),
Err(error) if is_recoverable_multi_open_error(error.as_ref()) => {
app.finish_open(&pod_name, Err(error.as_ref()));
}
Err(error) => {
let _ = leave_fullscreen(&mut terminal);
return Err(error);
}
}
app.reload().await?;
}
}
}
}
fn is_recoverable_multi_open_error(error: &(dyn std::error::Error + 'static)) -> bool {
error.is::<spawn::SpawnError>() || error.is::<NestedOpenCancelled>()
}
async fn run_spawn(resume_from: Option<SegmentId>) -> Result<(), Box<dyn std::error::Error>> {
let ready = match spawn::run(resume_from).await? {
SpawnOutcome::Ready(r) => r,
@ -396,16 +473,34 @@ async fn run_spawn(resume_from: Option<SegmentId>) -> Result<(), Box<dyn std::er
result
}
fn enter_fullscreen() -> Result<Terminal<CrosstermBackend<io::Stdout>>, Box<dyn std::error::Error>>
{
fn enter_fullscreen() -> Result<FullscreenTerminal, Box<dyn std::error::Error>> {
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
Ok(Terminal::new(backend)?)
}
fn enter_fullscreen_existing(
terminal: &mut FullscreenTerminal,
) -> Result<(), Box<dyn std::error::Error>> {
execute!(
terminal.backend_mut(),
EnterAlternateScreen,
EnableMouseCapture
)?;
Ok(())
}
fn leave_fullscreen(terminal: &mut FullscreenTerminal) -> io::Result<()> {
execute!(
terminal.backend_mut(),
DisableMouseCapture,
LeaveAlternateScreen
)
}
async fn run(
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
terminal: &mut FullscreenTerminal,
pod_name: String,
socket_path: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
@ -438,7 +533,7 @@ const POD_EVENT_DRAIN_LIMIT: usize = 32;
struct TerminalEventReader {
stop: Arc<AtomicBool>,
_thread: thread::JoinHandle<()>,
thread: Option<thread::JoinHandle<()>>,
}
impl TerminalEventReader {
@ -453,7 +548,7 @@ impl TerminalEventReader {
Ok((
Self {
stop,
_thread: thread,
thread: Some(thread),
},
rx,
))
@ -463,6 +558,9 @@ impl TerminalEventReader {
impl Drop for TerminalEventReader {
fn drop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(thread) = self.thread.take() {
let _ = thread.join();
}
}
}

View File

@ -62,37 +62,41 @@ impl From<session_store::StoreError> for MultiPodError {
pub(crate) enum MultiPodOutcome {
Quit,
Open {
pod_name: String,
socket_override: Option<PathBuf>,
},
Open(OpenPodRequest),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct OpenPodRequest {
pub(crate) pod_name: String,
pub(crate) socket_override: Option<PathBuf>,
}
pub(crate) async fn load_app() -> Result<MultiPodApp, MultiPodError> {
MultiPodApp::load(None).await
}
pub(crate) async fn run(
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
app: &mut MultiPodApp,
) -> Result<MultiPodOutcome, MultiPodError> {
let mut app = MultiPodApp::load(None).await?;
if app.list.entries.is_empty() {
return Err(MultiPodError::NoPods);
}
loop {
terminal.draw(|f| draw(f, &mut app))?;
terminal.draw(|f| draw(f, app))?;
match read()? {
TermEvent::Key(key) => match app.handle_key(key) {
MultiPodAction::None => {}
MultiPodAction::Quit => return Ok(MultiPodOutcome::Quit),
MultiPodAction::Open => {
if let Some(entry) = app.list.selected_entry() {
return Ok(MultiPodOutcome::Open {
pod_name: entry.name.clone(),
socket_override: entry.attach_socket_path().map(PathBuf::from),
});
if let Some(request) = app.prepare_open() {
return Ok(MultiPodOutcome::Open(request));
}
}
MultiPodAction::Refresh => app.reload().await?,
MultiPodAction::Send(request) => {
terminal.draw(|f| draw(f, &mut app))?;
terminal.draw(|f| draw(f, app))?;
let result = send_run_and_confirm(&request.socket_path, request.segments).await;
app.finish_send(result);
let _ = app.reload().await;
@ -147,7 +151,7 @@ impl MultiPodApp {
Ok(app)
}
async fn reload(&mut self) -> Result<(), MultiPodError> {
pub(crate) async fn reload(&mut self) -> Result<(), MultiPodError> {
self.list = load_pod_list(self.list.selected_name.clone()).await?;
self.ensure_selection_visible();
Ok(())
@ -211,6 +215,40 @@ impl MultiPodApp {
}
}
pub(crate) fn prepare_open(&mut self) -> Option<OpenPodRequest> {
let entry = match self.list.selected_entry() {
Some(entry) => entry,
None => {
self.notice = Some("No Pod is selected.".to_string());
return None;
}
};
if !entry.actions.can_open {
self.notice = Some("Selected Pod cannot be opened from this view.".to_string());
return None;
}
self.notice = Some(format!("Opening {}", entry.name));
Some(OpenPodRequest {
pod_name: entry.name.clone(),
socket_override: entry.attach_socket_path().map(PathBuf::from),
})
}
pub(crate) fn finish_open(
&mut self,
pod_name: &str,
result: Result<(), &dyn std::fmt::Display>,
) {
match result {
Ok(()) => {
self.notice = Some(format!("Returned from {pod_name}."));
}
Err(error) => {
self.notice = Some(format!("Open failed for {pod_name}: {error}"));
}
}
}
pub(crate) fn prepare_send(&mut self) -> Option<DirectSendRequest> {
let entry = match self.list.selected_entry() {
Some(entry) => entry,
@ -1114,6 +1152,53 @@ mod tests {
assert!(app.notice.as_deref().unwrap().contains("Delivered"));
}
#[test]
fn multi_open_request_keeps_dashboard_state_for_nested_single_pod() {
let mut app = test_app(vec![live_info("alpha", PodStatus::Idle)]);
app.input.insert_str("draft survives open");
let request = app.prepare_open().unwrap();
assert_eq!(request.pod_name, "alpha");
assert_eq!(
request.socket_override,
Some(PathBuf::from("/tmp/alpha.sock"))
);
assert_eq!(app.list.selected_entry().unwrap().name, "alpha");
assert_eq!(input_text(&app), "draft survives open");
assert!(app.notice.as_deref().unwrap().contains("Opening alpha"));
}
#[test]
fn multi_open_failure_keeps_composer_and_sets_notice() {
let mut app = test_app(vec![live_info("alpha", PodStatus::Idle)]);
app.input.insert_str("keep this draft");
let before = input_text(&app);
let error = io::Error::other("boom");
app.finish_open("alpha", Err(&error));
assert_eq!(input_text(&app), before);
assert_eq!(app.list.selected_entry().unwrap().name, "alpha");
assert!(
app.notice
.as_deref()
.unwrap()
.contains("Open failed for alpha")
);
}
#[test]
fn multi_open_disabled_target_stays_in_dashboard() {
let mut live = live_info("unreachable", PodStatus::Idle);
live.reachable = false;
live.status = None;
let mut app = test_app(vec![live]);
assert!(app.prepare_open().is_none());
assert!(app.notice.as_deref().unwrap().contains("cannot be opened"));
}
fn test_app(live: Vec<LivePodInfo>) -> MultiPodApp {
app_with_list(PodList::from_sources(
PodVisibilitySource::ResumePicker,

View File

@ -40,7 +40,30 @@ rustPlatform.buildRustPackage rec {
filter = sourceFilter;
};
cargoLock.lockFile = ./Cargo.lock;
cargoHash = "sha256-8ZT5moKFxj/5vbp5rsUG7UkPLY1fvQKhYTyjRWQ58xk=";
depsExtraArgs = {
# nixpkgs 25.11's fetchCargoVendor still uses crates.io's API
# download endpoint in this environment, which returns 403 while the
# immutable static CDN endpoint works. Keep this local package build on
# static.crates.io until the upstream fetcher is fixed in our nixpkgs pin.
buildPhase = ''
runHook preBuild
if [ -n "''${cargoRoot-}" ]; then
cd "$cargoRoot"
fi
vendor_util="$(command -v fetch-cargo-vendor-util-v2 || command -v fetch-cargo-vendor-util)"
cp "$vendor_util" ./fetch-cargo-vendor-util-static
substituteInPlace ./fetch-cargo-vendor-util-static \
--replace-fail 'https://crates.io/api/v1/crates/{pkg["name"]}/{pkg["version"]}/download' \
'https://static.crates.io/crates/{pkg["name"]}/{pkg["version"]}/download'
./fetch-cargo-vendor-util-static create-vendor-staging ./Cargo.lock "$out"
runHook postBuild
'';
};
strictDeps = true;

View File

@ -5,3 +5,10 @@ When searching, use grep/glob primitives rather than shell pipelines.
You can run multiple tools simultaneously by calling them within a single response.
It is recommended to run tools that handle asynchronous processing, such as queries and readings, in batches.
### Memory and knowledge
For past decisions, prior requests, durable preferences, project history, or why something was done, use targeted lookup instead of guessing from vague recollection.
Use `MemoryQuery` for durable memory records (summary, decisions, requests), `KnowledgeQuery` for project knowledge, `MemoryRead(kind=summary)` for the full memory summary, and `MemoryRead` on returned slugs when 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 authoritative for exact current state.
Do not query memory every turn, and normally prefer read/query tools; use `MemoryWrite`, `MemoryEdit`, or `MemoryDelete` only when explicitly asked or in a memory maintenance worker.

View File

@ -2,12 +2,12 @@
id: 20260527-000005-memory-tool-guidance-prompt
slug: memory-tool-guidance-prompt
title: プロンプト: memory / knowledge tool 利用タイミングのガイダンス
status: open
status: closed
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:05Z
updated_at: 2026-05-27T00:00:05Z
updated_at: 2026-05-28T23:59:06Z
assignee: null
legacy_ticket: tickets/memory-tool-guidance-prompt.md
---

View File

@ -0,0 +1,87 @@
---
id: 20260527-000005-memory-tool-guidance-prompt
slug: memory-tool-guidance-prompt
title: プロンプト: memory / knowledge tool 利用タイミングのガイダンス
status: closed
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:05Z
updated_at: 2026-05-28T23:59:06Z
assignee: null
legacy_ticket: tickets/memory-tool-guidance-prompt.md
---
## Migration reference
- legacy_ticket: tickets/memory-tool-guidance-prompt.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# プロンプト: memory / knowledge tool 利用タイミングのガイダンス
## 背景
通常 Pod には `MemoryQuery` / `MemoryRead` / `KnowledgeQuery` / `MemoryWrite` 等の memory / knowledge tools が提供されているが、現状の通常 system prompt はそれらを「いつ使うべきか」をほとんど説明していない。
現在の `resources/prompts/common/tool-usage.md` は、既知パスなら Read、検索なら Grep/Glob、並列可能ならまとめる、という汎用 tool 方針に留まる。memory / knowledge tools の description には操作方法はあるが、モデルが自発的に memory lookup すべき状況は明示されていない。
このため、過去の決定・ユーザー嗜好・以前の経緯を問われても、モデルが `MemoryQuery` / `MemoryRead` を自発的に使わない可能性が高い。`summary.md` resident injection により短い durable context は常時見えるようになるが、詳細な過去判断や request を探すには query guidance が必要である。
## 方針
通常 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.
```
文言は実装時に自然に調整してよいが、以下の意味は維持する。
- 過去判断 / 過去依頼 / ユーザー嗜好 / project history / why 系では memory lookup を促す。
- `MemoryQuery`, `KnowledgeQuery`, `MemoryRead(kind=summary)`, slug read の役割を明示する。
- resident context は stale になり得ると明示する。
- current user instruction / files / tickets / git / session logs の方が exact current state では強いと明示する。
- 毎 turn query しないと明示する。
## 要件
- 通常 Pod の default prompt に memory / knowledge tool 利用タイミングの guidance が入る。
- internal prompts (`memory_extract_system`, `memory_consolidation_system`, `compact_system`) の挙動を変えない。
- guidance は短く、通常 turn の token overhead を過度に増やさない。
- guidance は memory / knowledge を current authority より上に置かない。
- guidance は毎 turn memory query を促さない。
- `MemoryWrite` / `MemoryEdit` / `MemoryDelete` の自発的利用を安易に促さない。
- 通常作業では read/query を促し、write/edit/delete は明示的な依頼または memory maintenance worker に寄せる。
## 完了条件
- `resources/prompts/default.md` から memory guidance が render される。
- prompt render / catalog 関連 test があれば更新されている。
- internal worker prompt には不要な memory guidance が混ざらない。
- `cargo fmt --check` と関連 test が通る。
## 範囲外
- `summary.md` resident injection の実装。これは `memory-summary-resident-injection.md` で扱う。
- memory tool descriptions の大幅変更。
- memory usage metrics の設計変更。
- global memory / project local memory の store 分離。

View File

@ -0,0 +1,102 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:05Z -->
## Migrated
Migrated from tickets/memory-tool-guidance-prompt.md. No legacy review file was present at migration time.
---
<!-- event: close author: hare at: 2026-05-28T23:59:06Z status: closed -->
## Closed
---
id: 20260527-000005-memory-tool-guidance-prompt
slug: memory-tool-guidance-prompt
title: プロンプト: memory / knowledge tool 利用タイミングのガイダンス
status: closed
kind: task
priority: P2
labels: [migrated]
created_at: 2026-05-27T00:00:05Z
updated_at: 2026-05-28T23:59:06Z
assignee: null
legacy_ticket: tickets/memory-tool-guidance-prompt.md
---
## Migration reference
- legacy_ticket: tickets/memory-tool-guidance-prompt.md
- migrated_from: TODO.md / tickets directory migration on 2026-05-27
# プロンプト: memory / knowledge tool 利用タイミングのガイダンス
## 背景
通常 Pod には `MemoryQuery` / `MemoryRead` / `KnowledgeQuery` / `MemoryWrite` 等の memory / knowledge tools が提供されているが、現状の通常 system prompt はそれらを「いつ使うべきか」をほとんど説明していない。
現在の `resources/prompts/common/tool-usage.md` は、既知パスなら Read、検索なら Grep/Glob、並列可能ならまとめる、という汎用 tool 方針に留まる。memory / knowledge tools の description には操作方法はあるが、モデルが自発的に memory lookup すべき状況は明示されていない。
このため、過去の決定・ユーザー嗜好・以前の経緯を問われても、モデルが `MemoryQuery` / `MemoryRead` を自発的に使わない可能性が高い。`summary.md` resident injection により短い durable context は常時見えるようになるが、詳細な過去判断や request を探すには query guidance が必要である。
## 方針
通常 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.
```
文言は実装時に自然に調整してよいが、以下の意味は維持する。
- 過去判断 / 過去依頼 / ユーザー嗜好 / project history / why 系では memory lookup を促す。
- `MemoryQuery`, `KnowledgeQuery`, `MemoryRead(kind=summary)`, slug read の役割を明示する。
- resident context は stale になり得ると明示する。
- current user instruction / files / tickets / git / session logs の方が exact current state では強いと明示する。
- 毎 turn query しないと明示する。
## 要件
- 通常 Pod の default prompt に memory / knowledge tool 利用タイミングの guidance が入る。
- internal prompts (`memory_extract_system`, `memory_consolidation_system`, `compact_system`) の挙動を変えない。
- guidance は短く、通常 turn の token overhead を過度に増やさない。
- guidance は memory / knowledge を current authority より上に置かない。
- guidance は毎 turn memory query を促さない。
- `MemoryWrite` / `MemoryEdit` / `MemoryDelete` の自発的利用を安易に促さない。
- 通常作業では read/query を促し、write/edit/delete は明示的な依頼または memory maintenance worker に寄せる。
## 完了条件
- `resources/prompts/default.md` から memory guidance が render される。
- prompt render / catalog 関連 test があれば更新されている。
- internal worker prompt には不要な memory guidance が混ざらない。
- `cargo fmt --check` と関連 test が通る。
## 範囲外
- `summary.md` resident injection の実装。これは `memory-summary-resident-injection.md` で扱う。
- memory tool descriptions の大幅変更。
- memory usage metrics の設計変更。
- global memory / project local memory の store 分離。
---

View File

@ -0,0 +1,80 @@
---
id: 20260528-233524-multi-pod-open-return
slug: multi-pod-open-return
title: Return to multi-Pod view after opening a Pod
status: closed
kind: task
priority: P2
labels: [tui, pod, ux]
created_at: 2026-05-28T23:35:24Z
updated_at: 2026-05-28T23:57:49Z
assignee: null
legacy_ticket: null
---
## Background
`tui --multi` can open the selected Pod with `o`. The current implementation treats this as leaving the multi-Pod dashboard and tail-calling the normal single-Pod TUI. Once the single-Pod screen is detached with `Ctrl+D` / `Ctrl+C`, the process exits instead of returning to the multi-Pod view.
For now, no special "back mode" or dedicated back key is needed. The desired behavior is simpler: when the user opens a Pod from the multi-Pod dashboard, the normal single-Pod attach screen runs as a nested attach session. When that session exits normally by detach/quit (`Ctrl+D`, `Ctrl+C`, or equivalent normal exit), control returns to the multi-Pod dashboard.
This should be implemented by abstracting the TUI launch/control flow, not by adding protocol features or making the single-Pod App deeply aware of multi-Pod mode.
## Requirements
- `tui --multi` remains the entrypoint for the multi-Pod dashboard.
- In `tui --multi`, pressing `o` on a selected openable Pod opens the normal single-Pod conversation screen.
- When that single-Pod screen exits normally, TUI returns to the multi-Pod dashboard instead of exiting the process.
- `Ctrl+D` / `Ctrl+C` detach/quit from the opened single-Pod screen should return to multi view.
- Normal single-Pod launches such as `tui <pod>` / `tui --pod <name>` should continue to exit the process on `Ctrl+D` / `Ctrl+C`.
- Avoid introducing a dedicated back key or back mode for this ticket.
- The caller loop determines whether a normal single-Pod exit returns to multi view or exits the process.
- Preserve multi-Pod dashboard state where practical.
- The selected Pod name should remain selected after returning, if still visible.
- The multi-Pod composer contents should be preserved across open/return.
- The Pod list should refresh after returning so status changes are visible.
- Keep terminal handling clean.
- Do not unnecessarily leave/re-enter alternate screen between multi view and the nested single-Pod screen if the existing terminal can be reused safely.
- If reusing the same `Terminal`, ensure cursor/mouse/raw-mode cleanup remains correct on final exit and on errors.
- Error handling should be explicit.
- If opening the selected Pod fails before the single-Pod screen starts, show a multi-view notice and keep the dashboard active.
- If the single-Pod session exits with a fatal error, return that error or show a clear diagnostic according to the existing TUI error behavior; do not silently swallow fatal failures.
- Existing `tui --multi` direct send behavior, section layout, and separator fix must continue to work.
## Suggested implementation direction
- Split the current single-Pod attach runner into a reusable function that accepts an existing `Terminal` and returns when the attached screen exits.
- Change `run_multi()` from one-shot `multi_pod::run(...).await -> Open -> run_pod_name(...)` into a controller loop:
```text
loop:
run multi dashboard with previous app state / selected Pod
Quit => exit process
Open(pod) => run single-Pod attach screen using the same terminal
on normal exit, refresh dashboard and continue loop
```
- Avoid adding a new protocol method. This is local TUI orchestration.
- Avoid making `App` carry a generic `BackToMulti` mode unless it is strictly necessary; prefer caller-owned control flow.
## Acceptance criteria
- From `tui --multi`, pressing `o` opens the selected Pod's normal conversation screen.
- Pressing `Ctrl+D` / `Ctrl+C` in that opened screen returns to the multi-Pod dashboard.
- Starting a single-Pod TUI directly still exits on `Ctrl+D` / `Ctrl+C`.
- Returning to multi view preserves multi composer contents and selected Pod name when possible.
- Returning to multi view refreshes Pod status/list.
- Opening failure from multi view leaves the user in multi view with a visible notice.
- Existing multi-Pod tests still pass.
- Focused tests cover the controller/runner behavior where possible, especially distinguishing direct single-Pod launch from multi-owned nested launch.
- `cargo fmt --check`
- `cargo test -p tui multi --no-default-features` or equivalent focused tests.
- `cargo check -p tui -p client -p pod`
## Out of scope
- Dedicated back key.
- Per-Pod detail panes inside the multi-Pod dashboard.
- Protocol changes.
- Changing direct-send semantics.
- Changing Pod visibility/discovery rules.

View File

@ -0,0 +1,80 @@
---
id: 20260528-233524-multi-pod-open-return
slug: multi-pod-open-return
title: Return to multi-Pod view after opening a Pod
status: closed
kind: task
priority: P2
labels: [tui, pod, ux]
created_at: 2026-05-28T23:35:24Z
updated_at: 2026-05-28T23:57:49Z
assignee: null
legacy_ticket: null
---
## Background
`tui --multi` can open the selected Pod with `o`. The current implementation treats this as leaving the multi-Pod dashboard and tail-calling the normal single-Pod TUI. Once the single-Pod screen is detached with `Ctrl+D` / `Ctrl+C`, the process exits instead of returning to the multi-Pod view.
For now, no special "back mode" or dedicated back key is needed. The desired behavior is simpler: when the user opens a Pod from the multi-Pod dashboard, the normal single-Pod attach screen runs as a nested attach session. When that session exits normally by detach/quit (`Ctrl+D`, `Ctrl+C`, or equivalent normal exit), control returns to the multi-Pod dashboard.
This should be implemented by abstracting the TUI launch/control flow, not by adding protocol features or making the single-Pod App deeply aware of multi-Pod mode.
## Requirements
- `tui --multi` remains the entrypoint for the multi-Pod dashboard.
- In `tui --multi`, pressing `o` on a selected openable Pod opens the normal single-Pod conversation screen.
- When that single-Pod screen exits normally, TUI returns to the multi-Pod dashboard instead of exiting the process.
- `Ctrl+D` / `Ctrl+C` detach/quit from the opened single-Pod screen should return to multi view.
- Normal single-Pod launches such as `tui <pod>` / `tui --pod <name>` should continue to exit the process on `Ctrl+D` / `Ctrl+C`.
- Avoid introducing a dedicated back key or back mode for this ticket.
- The caller loop determines whether a normal single-Pod exit returns to multi view or exits the process.
- Preserve multi-Pod dashboard state where practical.
- The selected Pod name should remain selected after returning, if still visible.
- The multi-Pod composer contents should be preserved across open/return.
- The Pod list should refresh after returning so status changes are visible.
- Keep terminal handling clean.
- Do not unnecessarily leave/re-enter alternate screen between multi view and the nested single-Pod screen if the existing terminal can be reused safely.
- If reusing the same `Terminal`, ensure cursor/mouse/raw-mode cleanup remains correct on final exit and on errors.
- Error handling should be explicit.
- If opening the selected Pod fails before the single-Pod screen starts, show a multi-view notice and keep the dashboard active.
- If the single-Pod session exits with a fatal error, return that error or show a clear diagnostic according to the existing TUI error behavior; do not silently swallow fatal failures.
- Existing `tui --multi` direct send behavior, section layout, and separator fix must continue to work.
## Suggested implementation direction
- Split the current single-Pod attach runner into a reusable function that accepts an existing `Terminal` and returns when the attached screen exits.
- Change `run_multi()` from one-shot `multi_pod::run(...).await -> Open -> run_pod_name(...)` into a controller loop:
```text
loop:
run multi dashboard with previous app state / selected Pod
Quit => exit process
Open(pod) => run single-Pod attach screen using the same terminal
on normal exit, refresh dashboard and continue loop
```
- Avoid adding a new protocol method. This is local TUI orchestration.
- Avoid making `App` carry a generic `BackToMulti` mode unless it is strictly necessary; prefer caller-owned control flow.
## Acceptance criteria
- From `tui --multi`, pressing `o` opens the selected Pod's normal conversation screen.
- Pressing `Ctrl+D` / `Ctrl+C` in that opened screen returns to the multi-Pod dashboard.
- Starting a single-Pod TUI directly still exits on `Ctrl+D` / `Ctrl+C`.
- Returning to multi view preserves multi composer contents and selected Pod name when possible.
- Returning to multi view refreshes Pod status/list.
- Opening failure from multi view leaves the user in multi view with a visible notice.
- Existing multi-Pod tests still pass.
- Focused tests cover the controller/runner behavior where possible, especially distinguishing direct single-Pod launch from multi-owned nested launch.
- `cargo fmt --check`
- `cargo test -p tui multi --no-default-features` or equivalent focused tests.
- `cargo check -p tui -p client -p pod`
## Out of scope
- Dedicated back key.
- Per-Pod detail panes inside the multi-Pod dashboard.
- Protocol changes.
- Changing direct-send semantics.
- Changing Pod visibility/discovery rules.

View File

@ -0,0 +1,95 @@
<!-- event: create author: tickets.sh at: 2026-05-28T23:35:24Z -->
## Created
Created by tickets.sh create.
---
<!-- event: close author: hare at: 2026-05-28T23:57:49Z status: closed -->
## Closed
---
id: 20260528-233524-multi-pod-open-return
slug: multi-pod-open-return
title: Return to multi-Pod view after opening a Pod
status: closed
kind: task
priority: P2
labels: [tui, pod, ux]
created_at: 2026-05-28T23:35:24Z
updated_at: 2026-05-28T23:57:49Z
assignee: null
legacy_ticket: null
---
## Background
`tui --multi` can open the selected Pod with `o`. The current implementation treats this as leaving the multi-Pod dashboard and tail-calling the normal single-Pod TUI. Once the single-Pod screen is detached with `Ctrl+D` / `Ctrl+C`, the process exits instead of returning to the multi-Pod view.
For now, no special "back mode" or dedicated back key is needed. The desired behavior is simpler: when the user opens a Pod from the multi-Pod dashboard, the normal single-Pod attach screen runs as a nested attach session. When that session exits normally by detach/quit (`Ctrl+D`, `Ctrl+C`, or equivalent normal exit), control returns to the multi-Pod dashboard.
This should be implemented by abstracting the TUI launch/control flow, not by adding protocol features or making the single-Pod App deeply aware of multi-Pod mode.
## Requirements
- `tui --multi` remains the entrypoint for the multi-Pod dashboard.
- In `tui --multi`, pressing `o` on a selected openable Pod opens the normal single-Pod conversation screen.
- When that single-Pod screen exits normally, TUI returns to the multi-Pod dashboard instead of exiting the process.
- `Ctrl+D` / `Ctrl+C` detach/quit from the opened single-Pod screen should return to multi view.
- Normal single-Pod launches such as `tui <pod>` / `tui --pod <name>` should continue to exit the process on `Ctrl+D` / `Ctrl+C`.
- Avoid introducing a dedicated back key or back mode for this ticket.
- The caller loop determines whether a normal single-Pod exit returns to multi view or exits the process.
- Preserve multi-Pod dashboard state where practical.
- The selected Pod name should remain selected after returning, if still visible.
- The multi-Pod composer contents should be preserved across open/return.
- The Pod list should refresh after returning so status changes are visible.
- Keep terminal handling clean.
- Do not unnecessarily leave/re-enter alternate screen between multi view and the nested single-Pod screen if the existing terminal can be reused safely.
- If reusing the same `Terminal`, ensure cursor/mouse/raw-mode cleanup remains correct on final exit and on errors.
- Error handling should be explicit.
- If opening the selected Pod fails before the single-Pod screen starts, show a multi-view notice and keep the dashboard active.
- If the single-Pod session exits with a fatal error, return that error or show a clear diagnostic according to the existing TUI error behavior; do not silently swallow fatal failures.
- Existing `tui --multi` direct send behavior, section layout, and separator fix must continue to work.
## Suggested implementation direction
- Split the current single-Pod attach runner into a reusable function that accepts an existing `Terminal` and returns when the attached screen exits.
- Change `run_multi()` from one-shot `multi_pod::run(...).await -> Open -> run_pod_name(...)` into a controller loop:
```text
loop:
run multi dashboard with previous app state / selected Pod
Quit => exit process
Open(pod) => run single-Pod attach screen using the same terminal
on normal exit, refresh dashboard and continue loop
```
- Avoid adding a new protocol method. This is local TUI orchestration.
- Avoid making `App` carry a generic `BackToMulti` mode unless it is strictly necessary; prefer caller-owned control flow.
## Acceptance criteria
- From `tui --multi`, pressing `o` opens the selected Pod's normal conversation screen.
- Pressing `Ctrl+D` / `Ctrl+C` in that opened screen returns to the multi-Pod dashboard.
- Starting a single-Pod TUI directly still exits on `Ctrl+D` / `Ctrl+C`.
- Returning to multi view preserves multi composer contents and selected Pod name when possible.
- Returning to multi view refreshes Pod status/list.
- Opening failure from multi view leaves the user in multi view with a visible notice.
- Existing multi-Pod tests still pass.
- Focused tests cover the controller/runner behavior where possible, especially distinguishing direct single-Pod launch from multi-owned nested launch.
- `cargo fmt --check`
- `cargo test -p tui multi --no-default-features` or equivalent focused tests.
- `cargo check -p tui -p client -p pod`
## Out of scope
- Dedicated back key.
- Per-Pod detail panes inside the multi-Pod dashboard.
- Protocol changes.
- Changing direct-send semantics.
- Changing Pod visibility/discovery rules.
---

View File

@ -1,7 +0,0 @@
<!-- event: migration author: tickets.sh-migration at: 2026-05-27T00:00:05Z -->
## Migrated
Migrated from tickets/memory-tool-guidance-prompt.md. No legacy review file was present at migration time.
---

View File

@ -0,0 +1,63 @@
---
id: 20260529-001326-rename-installed-binaries
slug: rename-installed-binaries
title: Rename installed binaries
status: open
kind: task
priority: P2
labels: [cli, packaging, tui, pod]
created_at: 2026-05-29T00:13:26Z
updated_at: 2026-05-29T00:13:26Z
assignee: null
legacy_ticket: null
---
## Background
The workspace crate names `tui` and `pod` are useful internally, but the installed command names are too generic for user environments. `tui` does not identify the application, and `pod` collides with common terminology and other tooling.
Use application-specific binary names for installed commands:
- `insomnia`: the main terminal UI / user entrypoint, currently built from the `tui` crate.
- `insomnia-pod`: the Pod CLI/runtime command, currently built from the `pod` crate.
This is a command name change, not a crate rename. Keep the Rust crate/package names `tui` and `pod` unless there is a separate design decision to rename crates.
## Requirements
- Rename Cargo binary outputs:
- `crates/tui` binary name becomes `insomnia`.
- `crates/pod` binary name becomes `insomnia-pod`.
- Do not add legacy `tui` / `pod` installed aliases unless a concrete internal dependency requires it and is documented.
- Update Nix packaging to install and check the new binary names.
- `$out/bin/insomnia`
- `$out/bin/insomnia-pod`
- Update flake apps to use the new command names.
- default app should run `insomnia`.
- expose app entries for `insomnia` and `insomnia-pod`.
- Update docs that instruct users to run `tui` / `pod` as installed commands.
- Keep references to crate/package names where they are explicitly Cargo package names, e.g. `cargo check -p tui`.
- Prefer `cargo run -p tui -- ...` in development docs if referring to crate-based development invocation, but installed usage should use `insomnia`.
- Audit code/tests/scripts for assumptions that installed binary names are `tui` or `pod`.
- Internal runtime process spawning must still work.
- If code intentionally uses Cargo package names, leave them unchanged.
- Keep CLI semantics unchanged except for command names.
## Acceptance criteria
- `cargo build -p tui -p pod` produces runnable binaries named `insomnia` and `insomnia-pod`.
- `cargo run -p tui -- --help` and `cargo run -p pod -- --help` still work as development package invocations.
- Installed/Nix package smoke checks look for `insomnia` and `insomnia-pod`, not `tui` and `pod`.
- `flake.nix` app outputs use the new binary names.
- User-facing docs no longer tell users to run installed commands as `tui` / `pod`.
- No legacy aliases are installed unless explicitly justified.
- `cargo fmt --check`
- Focused cargo checks/tests for affected crates, at least `cargo check -p tui -p pod`.
- Nix validation that does not require network where possible, e.g. `nix flake check --no-build`.
## Out of scope
- Renaming crates/packages from `tui` / `pod`.
- Changing CLI argument semantics.
- Changing Pod protocol or socket behavior.
- Publishing or Home Manager module changes.

View File

@ -0,0 +1,7 @@
<!-- event: create author: tickets.sh at: 2026-05-29T00:13:26Z -->
## Created
Created by tickets.sh create.
---