Skip to content

feat: reduce task management friction across 14 real-session pain points#2

Open
vsumner wants to merge 11 commits intotintinweb:masterfrom
vsumner:fix/node-review-findings
Open

feat: reduce task management friction across 14 real-session pain points#2
vsumner wants to merge 11 commits intotintinweb:masterfrom
vsumner:fix/node-review-findings

Conversation

@vsumner
Copy link

@vsumner vsumner commented Mar 18, 2026

Summary

After analyzing 8 real coding sessions (2026-03-17), we identified 14 recurring friction points in task management workflows. This PR addresses all of them.

Why

Tasks are the primary coordination mechanism between agents and humans. In practice, sessions were losing time to: excessive tool round-trips when creating task lists, missing status transitions (no way to skip irrelevant tasks), noisy nudge reminders during active work, and lack of visibility into agent budget/progress. On resume, orphaned in_progress tasks went unnoticed.

What changed

Workflow efficiency

  • TaskCreateMany for batch creation with intra-batch dependencies — eliminates repeated TaskCreate + TaskUpdate round-trips
  • Enhanced TaskCreate with blockedBy/blocks/status/clearCompleted in a single call
  • skipped task status (⊘) that unblocks dependents without marking work "completed"
  • Auto-clear completed tasks on new task creation (configurable)

Signal-to-noise

  • Nudge suppression while tasks are actively in_progress — no reminders during focused work
  • Configurable nudge interval (0 to disable entirely)
  • Collapsed completed tasks in widget when 3+ active tasks exist

Visibility

  • Budget/timeout display on active tasks (⏱ remaining, % budget used)
  • Orphaned task detection on session resume with one-time reminder
  • DFS-based cycle detection with clear error messages

Settings

  • Nudge interval and auto-clear toggles in /tasks settings menu

Test plan

  • npm run typecheck clean
  • 157/157 tests passing (130 existing + 27 new)
  • Manual: create tasks, verify widget collapse at 3+ active
  • Manual: skip a blocker, verify dependent unblocks
  • Manual: verify settings persist across menu interactions

vsumner added 2 commits March 18, 2026 10:26
…test coverage

- Replace busy-wait with Atomics.wait, hoist SharedArrayBuffer outside loop
- Cap process output buffer at 1MB with FIFO eviction
- Guard proc.pid undefined with -1 sentinel
- Store kill signal from close event
- Harden load() to re-throw non-ENOENT/non-SyntaxError errors
- Atomic file writes with temp file cleanup on failure
- Single-pass clearCompleted()
- Cache widget task list, avoid disk I/O on animation frames
- Pin typebox version, add engines field
- Add dispose() for extension cleanup (event listeners, timers, widget)
- Self-remove subagents:ready listener to prevent leak
- Accept AbortSignal in spawnSubagent with proper cleanup
- Track in-flight spawn RPC listeners for dispose cleanup
- Replace global cascadeConfig singleton with per-task Map
- Add ProcessTracker.remove() for memory cleanup
- Fix appendOutput byte accounting to use chunk.length
- Add 11 new tests (130 total): buffer cap, oversized chunk, pid=-1,
  remove(), ENOENT/corrupt JSON handling, dispose, AbortSignal
Implements friction-reducing improvements identified from 8 real sessions:

- Skipped status for tasks (type, icon, unblocking behavior)
- Nudge suppression when tasks are in_progress
- Configurable nudge interval and auto-clear settings
- TaskCreateMany for batch task creation
- Enhanced TaskCreate with blockedBy/blocks/status/clearCompleted
- formatTaskDetail helper (DRY across TaskCreate/TaskGet)
- Collapse completed tasks in widget (3+ active)
- Budget/timeout display in widget
- Orphaned task detection on session resume
- Cycle detection via DFS in task-store
- Status-at-creation support in store

Review fixes applied:
- Bug: widget totalShown now uses visibleTasks.length (fixes "and N more")
- DRY: createSingleTask helper shared by TaskCreate/TaskCreateMany
- DRY: isTerminalStatus helper for blocker resolution checks
- Simplify: orphan state collapsed to single pendingOrphanReminder
- Simplify: settings-menu if/else if chain with hoisted saveTasksConfig

157 tests passing, typecheck clean.
@vsumner vsumner changed the title fix: Node.js review findings — reliability, correctness, and test coverage feat: 14 session-driven improvements + 5 review fixes Mar 18, 2026
@vsumner vsumner changed the title feat: 14 session-driven improvements + 5 review fixes feat: reduce task management friction across 14 real-session pain points Mar 18, 2026
vsumner added 9 commits March 18, 2026 15:43
Thread model parameter through to spawnSubagent() for both initial
execution and auto-cascade — was accepted in schema but never
forwarded to spawn options.

Timeout handler now emits subagents:rpc:stop with the agent ID
as a best-effort cancellation request. Previously it only marked
the task completed locally without attempting to stop the running
agent.

Update README to reflect current API surface:
- 8 tools (was 7, missing TaskCreateMany)
- Document TaskCreateMany with parameter table
- Add missing TaskCreate params (blockedBy, blocks, status,
  clearCompleted)
- Add skipped to TaskUpdate status enum
- Add token_budget and timeout_ms to TaskExecute table
- 4 persisted settings (was 2)
- 165 tests (was 116)
Replace Atomics.wait in file lock with bounded spin-sleep (10ms ×
50 retries = 500ms max, down from 50ms × 100 = 5s). Eliminates
SharedArrayBuffer dependency and reduces worst-case event loop
stall by 10×. Lock path only fires for project-scoped stores.

Add 50ms read cache TTL to TaskStore — coalesces rapid get()/list()
calls within the same event loop tick. withLock() force-reads to
ensure consistency under mutation. save() updates timestamp to
avoid stale reads after writes.

Add shape validation to JSON.parse in task-store load() and
tasks-config loadTasksConfig(). Rejects structurally invalid files
(wrong types, missing fields) instead of trusting blindly.

Move TaskBudget interface from closure-scoped inline definition
in index.ts to types.ts for testability and reuse.

Improve empty catch blocks: distinguish ENOENT (expected) from
EACCES (surprising) in lock release and file cleanup. Document
expected error codes in process-tracker kill catches.

Add isolatedModules to tsconfig for type-strip compatibility.
Bump engines.node from >=18 to >=22 (pi's actual floor).

Document session_switch type cast with TODO for upstream fix.
Adds ping, createMany, and update RPC handlers on pi.events,
following the same scoped request/reply pattern as pi-subagents.
Enables extensions like plan-executor to create and update tasks
programmatically without going through the LLM tool layer.

Channels: tasks:rpc:ping, tasks:rpc:createMany, tasks:rpc:update
Broadcasts tasks:ready on init for late-loading extensions.
Add missing TaskStatus import — typecheck was broken on HEAD.

Revert spin-sleep to Atomics.wait in file lock. The spin-loop
replacement burned CPU while still blocking the event loop;
Atomics.wait blocks without CPU cost. Keep the reduced budget
(10ms × 50 = 500ms max, down from 5s).

Move tasks:ready broadcast after all RPC handlers are registered
so consumers that react to ready can immediately call any RPC.

Add payload validation to tasks:rpc:createMany and
tasks:rpc:update — silently ignore malformed events instead of
crashing on destructure. Return partialIds in createMany error
replies for partial mutation visibility.

Add 10 tests for tasks:rpc:* handlers: ping reply, createMany
with deps and clearCompleted, update success/failure, malformed
payload rejection, and ready-ordering guarantee.
Treat missing blockers as resolved (deleted = done) in
TaskExecute, auto-cascade, and TaskList display. Previously
TaskExecute treated a missing blocker as unresolved, which
permanently blocked tasks when clearCompleted removed the
referenced task in the same TaskCreateMany call.

All three code paths now use the same semantic: if the blocker
no longer exists in the store, the dependency is satisfied.

Surface dependency warnings (dangling refs, cycles) from
createSingleTask in TaskCreate, TaskCreateMany, and the
tasks:rpc:createMany handler. Previously these warnings were
silently dropped.
Completed/skipped tasks now auto-clear when new tasks are created
via TaskCreate or TaskCreateMany. Matches Claude Code's TodoWrite
pattern where the agent sends an empty list after finishing work.

Users who want to preserve completed tasks across batches can set
autoClearCompleted: false in .pi/tasks-config.json or pass
clearCompleted: false explicitly.
TaskCreate now accepts either single-task params ({subject,
description, ...}) or a tasks array for batch mode. Removes
TaskCreateMany as a separate tool, reducing the API surface
from 8 tools to 7 — matching Claude Code's tool set.

Batch mode preserves all existing functionality: sequential
IDs, intra-batch dependency wiring via blockedBy/blocks,
clearCompleted, and warning surfacing.

Single mode returns an error if neither subject nor tasks
array is provided.
Return 'Created 0 task(s).' for TaskCreate({ tasks: [] }) instead
of falling through to the confusing single-mode error message.

Update README test count to 181+ (was stale at 165).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant