Revert idle "claude foreground" filter — back to legacy 5s notify
Reverts in one combined commit: -9931a92(inline pane_id + watch list into bash script) -6772b8d(pivot per-distro → per-pane via TILETOPIA_PANE_ID env) -f51033a(original per-distro idle filter) End-to-end probe never worked correctly against the real running app even after fixing the wsl.exe-drops-positional-args bug. Probe script ran fine in isolation but kept returning false-negative when called through tiletopia's wsl.exe spawn. Rather than keep iterating, back out cleanly — pane behaviour is now the original "go idle after 5s of silence regardless of what's running." memory.md session log notes the lessons for a future retry: don't ship per-distro again (CLAUDE.md explicitly says multi-claude-per-distro is the primary use case); prove the probe end-to-end before wiring into the idle effect (a "Test probe" button in MCP panel would have caught this in minutes). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9931a92c5f
commit
50fbd0e531
7 changed files with 27 additions and 486 deletions
85
memory.md
85
memory.md
|
|
@ -29,8 +29,7 @@ Durable memory for this project. Read at session start, update before session en
|
|||
- [x] ~~**M4 — orchestration.** Broadcast input, idle notifications, Ctrl+K palette.~~ Done 2026-05-22.
|
||||
- [x] ~~**Auto-save debouncing.**~~ 500ms timer in `App.svelte` `$effect`.
|
||||
- [x] ~~**HMR distro picker reset.**~~ No longer an issue — per-pane distro selection.
|
||||
- [x] ~~**Idle detection: filter by "claude is foreground."** Currently every pane notifies after 5s silence, which fires too eagerly when the user is reading a `claude` response. Want to detect that `claude` (or any user-specified process) is actually running in the pane's shell before notifying.~~ Done 2026-05-26 — **per-pane** probe (revised from initial per-distro design which broke tiletopia's primary use case of multiple claude panes per distro). Each WSL spawn tags itself with `TILETOPIA_PANE_ID=<id>` propagated via `WSLENV`; the probe runs `pgrep -x claude` in the distro then reads each match's `/proc/<pid>/environ` for the matching marker. Cached 3s by `(distro, pane_id)` on the Rust side. WSL panes only; PS + SSH fall back to legacy always-notify. Watched list hardcoded to `["claude"]` — `[[user-watch-list]]` follow-up below.
|
||||
- [ ] **`[[user-watch-list]]` — user-configurable idle-suppress process list.** v1 hardcodes `DEFAULT_WATCH_PROCESSES = ["claude"]` in `src-tauri/src/probe.rs`. Move to a workspace-config field (or dedicated `watch.json`) so users can add `cargo`, `npm test`, `pytest`, etc. without a recompile. Two design notes: (1) the values are passed straight to `pgrep -x`, so user-supplied strings must be validated (no shell metachars / leading `-`) before reaching `probe_one`; (2) the cache key is currently just the distro name — if the watched-list becomes per-pane / per-workspace, key the cache by `(distro, sorted_watch_list)` to prevent stale answers.
|
||||
- [ ] **Idle detection: filter by "claude is foreground."** Currently every pane notifies after 5s silence, which fires too eagerly when the user is reading a `claude` response. Want to detect that `claude` (or any user-specified process) is actually running in the pane's shell before notifying. Needs a Rust-side probe over WSL: `wsl.exe -d <distro> ps --ppid <shell_pid> -o comm=`. Defer to a future polish pass.
|
||||
- [ ] **Native OS notifications.** Right now toasts only show while the app is focused. `tauri-plugin-notification` would push to Windows Action Center; useful for "claude finished" when the app is minimized. Worth adding if/when the user actually backgrounds the app while waiting for sessions.
|
||||
- [ ] **Configurable idle threshold.** Hardcoded 5000ms in `LeafPane.svelte`. Should move into a settings panel; M5 territory.
|
||||
- [x] ~~**Logic tests for `tree.ts`.**~~ Vitest, 43 cases, runs via `pnpm test`. Done 2026-05-22.
|
||||
|
|
@ -53,83 +52,25 @@ Durable memory for this project. Read at session start, update before session en
|
|||
|
||||
## Session log
|
||||
|
||||
### 2026-05-26 — Idle filter pivot: per-distro → per-pane (env-var marker)
|
||||
### 2026-05-26 — Backed out idle "claude foreground" filter (kept legacy 5s notify)
|
||||
|
||||
The per-distro probe shipped earlier today (see entry below) had the wrong granularity for tiletopia's actual use case. CLAUDE.md says the app is "built primarily to manage multiple `claude` sessions across projects in parallel" — i.e. multiple claude panes per distro is THE point. Per-distro suppression silenced every pane the moment one ran claude. Tested live: user saw all Ubuntu panes stop reporting idle because one pane (this session) was running claude.
|
||||
Shipped earlier today as per-distro, pivoted to per-pane via `TILETOPIA_PANE_ID` env marker, then a probe-script bug surfaced (positional args dropped by `wsl.exe -- bash -c "..." _ <id>`). Fixed the arg-passing by inlining values, but on real-app test the pane still showed idle while claude was running — and at that point the user (rightly) called credit waste and asked to back the whole feature out.
|
||||
|
||||
Fix: pivot to per-pane detection via env-var marker.
|
||||
**Reverted commits** (in one combined revert):
|
||||
- `9931a92` — inline pane_id / watch list into script (drop positional args)
|
||||
- `6772b8d` — pivot per-distro → per-pane via TILETOPIA_PANE_ID env marker
|
||||
- `f51033a` — original per-distro idle filter
|
||||
|
||||
**Mechanism:**
|
||||
Now back to "every pane goes idle after 5s of silence" — the behaviour that worked before today's fan-out attempt. The `[[user-watch-list]]` marker in the open-questions section is removed; the original idle-filter TODO is restored.
|
||||
|
||||
1. `pty.rs` — every WSL spawn now sets `TILETOPIA_PANE_ID=<id>` as a Windows-side env var on the `wsl.exe` invocation, plus `WSLENV=TILETOPIA_PANE_ID/u` (appended to any pre-existing WSLENV) so the var gets forwarded into the distro. Reserves the `id` BEFORE `build_command` instead of after (since the env tag needs to know the id at spawn time).
|
||||
2. `probe.rs` — rewritten. New shape: `is_watch_process_running(distro, pane_id)`. Runs a bash one-liner inside the distro that `pgrep -x <name>`s for each watched process, then for each PID checks `/proc/<pid>/environ` for an exact `TILETOPIA_PANE_ID=<target>` line (using `tr '\0' '\n' | grep -qxF`). Inheritance does the work — claude inherits env from the shell, shell inherits from wsl.exe via WSLENV. Cache keyed by `(distro, pane_id)`.
|
||||
3. **Fail-safe inverted.** v1 returned `true` (suppress) on probe failure — meant a transient error silenced idle forever until the cache TTL turned over and re-failed. v2 returns `false` (don't suppress) — better to occasionally over-notify than permanently silence. Frontend `catch` also no longer flips to suppression.
|
||||
4. `commands.rs` + `ipc.ts` + `LeafPane.tsx` updated to thread `pane_id` through. LeafPane tracks the backend PaneId in a ref (`paneIdRef`), set by `onPaneSpawned`. Ticks before the spawn completes pass `0` — won't match any real pane's marker, so probe returns false and the pane idles normally.
|
||||
**Lessons for if/when we attempt this again:**
|
||||
|
||||
**Verification path** (user runs):
|
||||
- The per-distro design fundamentally doesn't fit tiletopia (CLAUDE.md: "manage multiple claude sessions across projects in parallel"). Don't ship per-distro again.
|
||||
- Per-pane via env-var marker is the right shape, BUT the probe still didn't work end-to-end in the real app even after the inline-args fix. The `pgrep` exit + `/proc/<pid>/environ` reads worked in isolation (verified manually from PowerShell) — something about how tiletopia's `wsl.exe` spawn differs from a manual invocation. Could be: stdin handling, working directory, environment context. Worth a from-scratch design rather than another fix-on-fix iteration.
|
||||
- If we retry, prove the probe end-to-end against the running app FIRST (e.g. add a temporary "Test probe" button in the MCP panel that calls the Tauri command and shows the result) before wiring it into the idle effect. Validates the whole IPC path without the timing complications of the idle tick.
|
||||
|
||||
```powershell
|
||||
# In one Ubuntu pane: launch claude. Wait 5s.
|
||||
# Expect: red border does NOT appear (this pane has claude).
|
||||
# In another Ubuntu pane: do nothing. Wait 5s.
|
||||
# Expect: red border DOES appear (this pane has no claude).
|
||||
# Exit claude in the first pane. Wait 5s.
|
||||
# Expect: red border appears there too.
|
||||
```
|
||||
Restored the original idle-filter open question in the TODO section.
|
||||
|
||||
**Files touched:**
|
||||
|
||||
- `src-tauri/src/pty.rs` — env tagging on WSL spawns (~25 lines).
|
||||
- `src-tauri/src/probe.rs` — rewritten (~150 lines, similar size).
|
||||
- `src-tauri/src/commands.rs` — sig change (1 extra arg).
|
||||
- `src/ipc.ts` — sig change + doc comment.
|
||||
- `src/lib/layout/LeafPane.tsx` — paneIdRef + pass to probe call + updated comments.
|
||||
|
||||
**Validated:** `pnpm check` clean. Rust validation needs `cargo build / cargo test --lib` from Windows.
|
||||
|
||||
Open follow-ups specific to this session:
|
||||
|
||||
- **WSLENV escaping.** If a user has `WSLENV` already set with weird chars (spaces, semicolons, embedded `:`), the simple `format!("{existing}:TILETOPIA_PANE_ID/u")` may or may not behave as expected. Most users have no WSLENV set; if it becomes an issue, parse/validate before appending.
|
||||
- **Probe ergonomics on minimal distros.** New fail-safe is "no match" instead of "suppress", so a distro missing `pgrep` or `bash` (rare but possible for stripped Alpine etc.) just gets always-notify. Acceptable; document if anyone hits it.
|
||||
- **Tagging existing panes.** The env tag only applies to NEW spawns. Panes already running from before this change won't have the marker — they'll always show idle (since the probe won't find their TILETOPIA_PANE_ID). User needs to close + respawn each WSL pane once after deploying this fix. Worth mentioning in the upgrade note if we ever cut a release.
|
||||
|
||||
### 2026-05-26 — Idle filter: suppress when `claude` is running in the distro
|
||||
|
||||
The idle indicator used to fire 5s after any silence, regardless of what the pane was doing. While the user reads a long `claude` response the pane is silent (claude is processing or the human is reading) and the red border + titlebar "N idle" count is just noise. Fixed: WSL panes now probe the backend before flagging idle, and stay quiet if `claude` is running anywhere in the distro.
|
||||
|
||||
**Granularity is per-distro, not per-pane.** Identifying which Windows pane corresponds to which Linux-side shell inside WSL is too complex (PIDs aren't visible from Windows; ProcMon-style probes are fragile). Agreed trade-off: if claude is running in distro X, ALL panes in distro X suppress. Over-suppression for multi-pane-same-distro users is fine — the previous always-notify bug was worse, and that user pattern is the minority.
|
||||
|
||||
**Architecture:**
|
||||
|
||||
1. New `src-tauri/src/probe.rs` module with `ProbeCache` — `parking_lot::Mutex<HashMap<String, (Instant, bool)>>` keyed by distro name, 3s TTL. Sized against the frontend's 1s idle-tick: ~one `wsl.exe` call per distro per 3 ticks even with many panes polling, while reacting to "claude finished" within a few seconds.
|
||||
2. Probe command runs `wsl.exe -d <distro> -- pgrep -x claude` via `quiet_command_pub` (new public alias of the existing `quiet_command` in pty.rs so cross-module callers don't re-implement the `CREATE_NO_WINDOW` dance). Exit 0 = match, exit 1 = no match, anything else = probe failure.
|
||||
3. **Fail-safe is suppression.** Any probe error (wsl.exe missing, distro stopped, pgrep not installed) resolves to `true` → frontend suppresses the idle indicator. Matches the agreed trade-off: over-suppression beats false-positive notifications.
|
||||
4. New Tauri command `is_watch_process_running(distro)`. Wrapped in `tokio::task::spawn_blocking` because the shell-out can take 100-300ms — keep it off the async runtime's thread pool.
|
||||
5. `LeafPane.tsx` idle-detection effect rewritten: when the tick says "now idle", branch by `shellKind`. WSL → probe backend, suppress if true. PowerShell + SSH → skip the probe and fall back to legacy behaviour (PS has no portable `ps`; SSH processes live on a remote box; out of scope for v1). Includes `inFlight` guard so a slow probe doesn't stack with subsequent ticks, and a `cancelled` flag for the React-18-StrictMode cleanup pattern we always use here.
|
||||
|
||||
**Watched list is currently hardcoded.** `DEFAULT_WATCH_PROCESSES: &[&str] = &["claude"]` in probe.rs. Comment marks the v2 follow-up: surface as a workspace-config field, key the cache by `(distro, sorted_list)` if it becomes per-pane, and validate user-supplied strings against `pgrep` shell-injection (no `-` prefix, no shell metachars).
|
||||
|
||||
**Files touched:**
|
||||
|
||||
- `src-tauri/src/probe.rs` — new module (~150 lines).
|
||||
- `src-tauri/src/pty.rs` — `quiet_command_pub` exposed for cross-module use.
|
||||
- `src-tauri/src/lib.rs` — register the module, the `ProbeCache` state, and the command in `invoke_handler`.
|
||||
- `src-tauri/src/commands.rs` — `is_watch_process_running` Tauri command.
|
||||
- `src/ipc.ts` — `isWatchProcessRunning` TS wrapper.
|
||||
- `src/lib/layout/LeafPane.tsx` — idle-detection effect now branches on shellKind and gates WSL transitions through the probe.
|
||||
|
||||
**Validated:**
|
||||
|
||||
- `pnpm check` clean (0 errors).
|
||||
- `pnpm test` clean (72 tree.ts tests pass — no UI tests yet, so the React-side change isn't covered automatically).
|
||||
- Rust side authored in WSL; user to run `cargo build / cargo check -p tiletopia_lib` from Windows before merging.
|
||||
|
||||
Open follow-ups specific to this session:
|
||||
|
||||
- **`[[user-watch-list]]` config surface.** See open-questions section above. Probably 30 min of work: add `watchProcesses?: string[]` to workspace.json, validate per-name (no `-`, no shell metachars, length cap), thread through to a new `is_watch_process_running_for` command that takes the list, key the cache by `(distro, sorted_list_hash)`.
|
||||
- **Probe latency-as-jitter.** First idle tick after 5s silence triggers a 100-300ms `wsl.exe` shell-out. The user sees the red border flicker on for ~one tick before the probe resolves and clears it. Not visually obvious in practice (the red is already a transient signal), but could pre-warm the cache on a slower interval if it bites.
|
||||
- **PowerShell idle filter.** PS has no `ps` equivalent we can probe cheaply; closest is `Get-Process` + a watched-list mapping (`claude` doesn't exist on Windows, but `cargo`, `npm`, `python` do). Defer until someone actually runs a long-running CLI in PS and complains.
|
||||
- **Workspace-edit migration of the `LeafPane.svelte` mention** in the open-question section about the 5000ms threshold — file says `.svelte` but we're React now. Drive-by, not done here ("don't refactor unrelated code").
|
||||
### 2026-05-26 — README shortcut table now generated from `shortcuts.ts`
|
||||
|
||||
The keyboard-shortcut table in README and the in-app help overlay used to be hand-mirrored copies maintained by "keep in sync" comments. They drifted (most recently the navigation/font-size entries diverged). Now `src/lib/shortcuts.ts` is the single source of truth and README's section is generated from it.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue