feat: automatic GC of stale team directories#8
Closed
RensTillmann wants to merge 5 commits into
Closed
Conversation
Closes tmustier#4. Compatible with PR tmustier#6 (onDm callback). Three notification layers: 1. Per-task: pi.sendMessage() injects completion info into leader context (triggerTurn: false) so it accumulates without interruption 2. Batch-complete: pi.sendUserMessage() wakes the idle leader when ALL tasks from a delegate() call finish 3. DMs (PR tmustier#6 compat): onDm callback with 50ms debounce batching, delivered via pi.sendMessage() with triggerTurn: true DelegationTracker class tracks task ID batches from delegate calls. Cleared on session_switch. Batches pruned after notification.
The original DelegationTracker.checkCompleted() polled listTasks() on every inbox cycle, which raced — it could see stale task statuses and fire false-positive batch-complete notifications immediately after delegation. Replaced with event-driven markCompleted(taskId): only marks a task done when an idle_notification with completedTaskId is actually received. No more filesystem polling for task status. Also: batch completions are collected across all messages in one poll cycle and deduplicated before firing notifications.
Add three-layer cleanup for team directories that accumulate across sessions: 1. **Startup GC** — on session_start, silently scan and remove stale team directories (no active attach claim, no online workers, no in-progress tasks). Fire-and-forget, never blocks the session. 2. **Exit cleanup** — on session_shutdown, delete the current session's team directory if it's empty (no tasks, no teammates). Best-effort. 3. **`/team gc` command** — manual bulk cleanup with `--dry-run` to preview and `--force` to skip confirmation. Same logic as startup GC but interactive. A team is considered dead (safe to remove) when ALL of: - Not the current session's team - No active (non-stale) attach claim - No online workers - No in-progress tasks New file: `team-gc.ts` with reusable `findGcCandidates()` and `gcTeamDirs()` functions that leverage existing `cleanupTeamDir()`, `listDiscoveredTeams()`, and `assessAttachClaimFreshness()` infrastructure.
…completion' into feature/team-gc
tmustier
pushed a commit
that referenced
this pull request
Mar 22, 2026
Two-layer automatic cleanup for team directories that accumulate across sessions: 1. **Startup GC** — on session_start, fire-and-forget call to the existing gcStaleTeamDirs() with a 24h age floor. Reuses the well-tested function that checks age + online workers + in-progress tasks + attach claim freshness. Never blocks startup. 2. **Exit cleanup** — on session_shutdown, delete the current session's team directory if it's empty (no tasks in any namespace, no teammates were active, no fresh attach claim from another session). Best-effort, never blocks shutdown. Only cleans the session's own team (not attached teams). Based on #8 by @RensTillmann.
tmustier
added a commit
that referenced
this pull request
Mar 22, 2026
) Incorporates the safe parts of PR #8 (@RensTillmann). **Startup GC:** fire-and-forget gcStaleTeamDirs() on session_start with 24h age floor, excluding the current session's team. **Exit cleanup:** delete own empty team dir on shutdown — checks all task namespaces, attach claims, config workers, and RPC teammates before deleting. Co-authored-by: Rens Tillmann <rens@inb0x.ai>
Owner
|
The safe parts of this PR (startup GC + exit cleanup) have been incorporated into #30 with author attribution. The state-based |
Owner
|
Hey @RensTillmann — cheers for this too, and sorry it took so long! The safe parts are implemented with credit in #30 (you're commit author + co-author on the squash). The new behaviour is:
We didn't take the state-only Shipped in v0.5.1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Every pi session creates a team directory under
~/.pi/agent/teams/. These are never cleaned up, accumulating indefinitely. On a moderately active system this quickly results in dozens of stale directories.Solution
Three-layer cleanup:
Startup GC — on
session_start, silently scan and remove stale team directories. Fire-and-forget, never blocks the session.Exit cleanup — on
session_shutdown, delete the current session's team directory if it's empty (no tasks, no teammates). Best-effort./team gccommand — manual bulk cleanup with--dry-runto preview and--forceto skip confirmation.What makes a team "dead" (safe to remove)?
All of the following must be true:
New file:
team-gc.tsReusable
findGcCandidates()andgcTeamDirs()functions that leverage existing infrastructure (cleanupTeamDir(),loadTeamConfig(),assessAttachClaimFreshness(),listTasks()).Changes
extensions/teams/team-gc.tsextensions/teams/leader.tsextensions/teams/leader-lifecycle-commands.ts/team gccommand handlerextensions/teams/leader-team-command.tsgcsubcommand + help textskills/agent-teams/SKILL.mdREADME.mdChecks
tsctypecheck passeseslintlint passes