Skip to content

feat: subtask UX, PR badge refresh, batch delete, notification & terminal size fixes#81

Open
kovtcharov-amd wants to merge 47 commits into
mainfrom
feat/unified-todo-tracker
Open

feat: subtask UX, PR badge refresh, batch delete, notification & terminal size fixes#81
kovtcharov-amd wants to merge 47 commits into
mainfrom
feat/unified-todo-tracker

Conversation

@kovtcharov-amd

Copy link
Copy Markdown
Collaborator

Summary

  • Subtask toggle inline: collapse/expand button now lives inside the parent task row as a pill badge (▶ 5) — no separate row, no wasted space
  • PR badge refresh on hover/click: hovering or clicking a task immediately re-fetches PR state from GitHub — merged PRs and CI results now appear without waiting for the 30s interval
  • PR refresh bug fix: worktree workspaces were using their own path as the repo root for gh pr list — now correctly uses the parent repo path
  • Batch delete modal: multiple claudia_delete_task MCP calls accumulate into a single modal with checkboxes instead of sequential popups
  • Bypass permissions fix: permissionMode=bypassPermissions now correctly flows to --dangerously-skip-permissions, --allowedTools *, and settings.local.json for subtasks
  • Notification fix: replace undefined --background-secondary CSS var with solid var(--bg-tertiary) + color-mix tints — notifications were transparent
  • Terminal size fix: new tasks now spawn with the real measured FitAddon dimensions instead of a pixel-math guess (clientWidth/9), eliminating the narrow-width-on-creation bug

Test plan

  • Spawn an orchestrator task with isolated subtasks — verify collapse toggle appears inline in the parent row and works
  • Open a PR, merge it, hover the task badge — verify it updates to merged within seconds
  • Have an agent call claudia_delete_task on multiple tasks simultaneously — verify single modal with checkbox list appears
  • Create a new task — verify terminal width matches the window, no resize needed
  • Enable bypass permissions, spawn a subtask — verify it doesn't prompt for permissions

Ovtcharov and others added 30 commits May 11, 2026 15:40
showBrowseButton was hardcoded to false, preventing users from using
the native folder picker. Set to true so the Browse button appears
and opens the OS folder dialog via the existing WebSocket handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add drag-and-drop support on the workspace panel: drop a folder from
  the OS file explorer to add it as a workspace. In Electron, the full
  path is extracted directly. In the browser, opens the path input modal.
- Fix Browse button: use REST endpoint instead of blocking WebSocket
  execFileSync which froze the server. Only show Browse in Electron mode
  where the native dialog works reliably.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: syncWorkspaceMcpConfigs wrote .mcp.json to the claudia
project root on every startup, triggering tsx watch to restart the
server in an infinite loop. Now skips syncing to Claudia's own
workspace directory.

Also:
- Fix Browse button: add -STA flag for Windows PowerShell folder dialog,
  remember last browsed path across sessions, kdialog fallback on Linux
- Re-enable Browse button in Add Workspace dialog
- Fix drag-and-drop: only activate for external OS drops (Files type),
  internal workspace reordering drags pass through unaffected

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 512KB caps were too aggressive — users lost scrollback history
after rotation. Disk files now cap at 10MB (rotate keeping 5MB tail),
and clients receive up to 2MB of history for scrollback. Memory loading
on reconnect remains capped at 512KB to prevent OOM.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a scrollbar appears/disappears during active output, the container
width changes by ~15px, flipping cols by 1-2. This caused Claude Code's
TUI to re-render at alternating widths, producing overlapping garbled
text.

Fix:
- Skip resize events where cols changed by <= 2 (scrollbar noise)
- Track last sent cols/rows to deduplicate
- Increase ResizeObserver debounce from 50ms to 150ms
- Use fitTerminal() (fit + refresh) to clear artifacts after resize

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
References #59

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d-turn

The previous policy only reconnected tasks with shouldContinue=true
(busy/starting at time of shutdown). This meant every tsx watch restart
disconnected all idle tasks (34 tasks showing 'disconnected'), requiring
manual clicks to reconnect each one.

Now reconnects ALL tasks that were active within the last 2 hours and
have a sessionId. Mid-turn tasks still get priority in reconnect order.
Batching (2 at a time with delays) prevents resource exhaustion.
1. TaskInputBar: queue messages when WebSocket is not open instead of
   silently dropping them. Retries every 500ms until WebSocket reconnects.
   Previously typing 'continue' while disconnected did nothing — the
   message stayed in the input but was never sent.

2. Start scripts: switch from 'dev' (tsx watch) to 'dev:no-watch' to
   prevent spurious server restarts from file changes (Claude Code tasks
   editing source files, antivirus, Windows indexer). The UI restart
   button still works for manual restarts.

3. CLAUDE.md: updated with current architecture, no-push-to-main rule,
   no attribution comments rule.
…disconnect

# Conflicts:
#	CLAUDE.md
#	frontend/src/components/TerminalView.tsx
Re-pasting on retry caused duplicate content because Claude Code wraps
long prompts across multiple lines — Ctrl+U only clears the current line,
leaving the rest. Now the prompt is written once; only Enter is retried.
…e, MCP guidance

- Worktree child workspaces are hidden from the sidebar and rendered
  inline within their parent workspace as collapsible groups
- Each group shows the branch name (or task name after Claude auto-titles)
  with a green dot for active tasks and a remove button
- Added isolate toggle button in the task input row (GitBranch icon)
  that creates a worktree for the next task when enabled
- Backend now reads `isolate` flag from task:create messages and creates
  a worktree when set (previously only workspace-level autoWorktree worked)
- When a task in a worktree is renamed, the worktree workspace displayName
  updates to match so the group header shows a human-readable label
- Added worktree isolation guidance to the MCP orchestration system prompt
  instructing Claude to use isolate:true for file-modifying parallel tasks
- Silent worktree creation failures now send a WS error to the client
Spawned tasks inherited ANTHROPIC_MODEL from the shell that launched the
server (e.g. a corporate proxy profile), which in interactive PTY mode
beat the --model flag -- so tasks silently ran the inherited model
regardless of the Claudia setting. getTaskEnvironment now overrides
ANTHROPIC_MODEL with the configured defaultModel so the Claudia setting
is authoritative.

Also: validate effortLevel/defaultModel config switches, and add a
-Watch switch to start.ps1 to opt into tsx auto-reload (default stays
no-watch for stability).
…croll

- resolveClaudeSpawn now detects the native bin/claude.exe shipped by newer
  @anthropic-ai/claude-code and spawns it directly. The old code looked only
  for cli.js, didn't find it, and fell back to `cmd.exe /c claude.cmd`, which
  re-parsed the command line and corrupted the multiline --system-prompt and
  the --model flag after it -- so spawned tasks ignored the configured model.
  Reverts the earlier ANTHROPIC_MODEL env override, which was the wrong fix.
- decodeHtmlEntities normalizes task display names (e.g. "notes &amp; CI" ->
  "notes & CI") at rename and on serialization, fixing legacy/agent-encoded
  names rendered as plain text in the UI.
- TerminalView guards scrollToLine against a non-integer viewportY (xterm
  throws "This API only accepts integers" on NaN), stopping an error loop.
Updates the injected orchestration guidance and the claudia_create_task
tool description to instruct agents to prefer spawning Claudia tasks over
launching their own internal subagents for delegatable work — Claudia
tasks are user-visible, monitorable, resumable, and worktree-isolated.
Internal subagents remain the fallback for quick inline lookups or when a
task would clearly produce a worse outcome.
PR badges:
- Resolve a workspace branch's GitHub PR via `gh` (getPrForBranch), cached
  in-memory on the workspace store (never persisted) and pushed to the UI.
- Refresh only active/unseen workspaces every 90s with reentrancy and
  per-workspace in-flight guards, to avoid `gh` process storms on slow-auth
  machines; also refresh on task create and workspace reset.
- New PrBadge component renders #number tinted by PR state (draft/open/
  merged/closed) with a subtle CI status mark, in the worktree group header
  and the main workspace header. Tooltip shows PR number, title, state, CI.

Backend restart:
- start.ps1/start.sh run the backend in a relaunch loop; exit code 75 (from
  POST /api/server/restart) restarts it, any other code stops. Fixes the
  restart button, which previously killed the backend with no recovery.
- start.ps1 caches the process Handle before WaitForExit so ExitCode is
  readable; full process-tree kill on exit frees ports and fixes stuck Ctrl+C.
- `dev` script sets CLAUDIA_WATCH_MODE so the endpoint touches index.ts under
  tsx watch but exits 75 in no-watch. gracefulShutdown takes an exit code and
  guards against double-shutdown.
When a task runs raw `git worktree add` (instead of Claudia's auto-worktree
path), the new worktree wasn't registered as a workspace and never showed in
the sidebar. discoverWorktrees() scans repos that have Claudia tasks, finds
git worktrees not yet known, and registers them via addWorktreeWorkspace +
broadcast. Triggered when a task goes idle and on a 60s periodic sweep, with
a concurrency guard. Scoped to repos with active tasks to avoid surfacing
unrelated pre-existing worktrees.
A worktree with a single task now renders inline as a normal task row with
a small worktree icon badge (branch in tooltip) plus a PR badge for its
branch, instead of a collapsible group section. Worktrees with 2+ tasks keep
the group (where the PR badge lives on the group header, so the per-task
badge would be redundant).
Rework worktree discovery: instead of mass-registering every worktree in a
repo (which floods repos with dozens of worktrees), detect when a task's
Claude session creates a NEW worktree by diffing the repo's worktree list
against a per-repo baseline taken at first scan. A branch that appears while
a task runs in that repo is attributed to the most-recently-active task and
the task row is annotated with sessionWorktreeBranch + PR info, showing the
worktree badge in place. The task's PTY cwd stays at the parent repo, so the
session's branch isn't otherwise visible.
Long task prompts sometimes arrived truncated at the start ("began
mid-sentence") because Claude Code's TUI input box repaints while the prompt
is being written, dropping leading characters. Wrap the initial-prompt write
in bracketed paste (ESC[200~ ... ESC[201~) so the terminal delivers it as one
atomic paste. Claude Code enables bracketed-paste mode (ESC[?2004h), so the
markers are consumed. Strip any stray end-marker in the prompt defensively.
A worktree workspace stored the branch it was created with, but Claude often
switches to or creates a different branch inside the worktree and opens the PR
there (e.g. claudia/task-xxxx -> fix/my-feature). PR lookup queried the stale
stored branch and found no PR, so the badge stayed empty. Now resolve the
worktree's CURRENT branch via getCurrentBranch(worktreePath), falling back to
the stored worktreeBranch.
The PR refresh only polled active-task workspaces on the 90s interval, so
when a task switched branches and went idle the badge stayed stale. Now
trigger refreshPrInfoFor on idle, same as worktree discovery, so the badge
updates promptly after Claude finishes a step that changed the branch.
Previously the PR badge only refreshed on the 90s interval, so branch
switches mid-task showed stale badges. Now refreshPrInfoFor does a fast
local getCurrentBranch check on every idle/busy state change and only
calls the expensive gh API when the branch actually changed. The periodic
interval uses force=true to also catch CI status updates on unchanged
branches.
The dropdown menu used position:absolute inside an overflow-y:auto container,
so it got clipped when near the bottom. Switched to position:fixed and
calculate coordinates from the trigger button's viewport rect. The menu
closes on scroll since its fixed position would detach from the trigger.
… prompt

The initial-prompt path used bracketed paste to prevent front-truncation,
but the writeToTask follow-up path (line 3404) still wrote raw text. Large
pasted messages (e.g. release notes with markdown/emoji) got truncated the
same way. Now both paths wrap in ESC[200~...ESC[201~.
Archived tasks were rendered in insertion order (oldest first) and showed
the raw prompt text instead of the displayName set by the agent. Now sorted
by lastActivity descending and uses displayName when available.
Messages sent while a task is busy were written as raw text without
bracketed paste, causing the same truncation/loss as the initial-prompt
bug. Now all multi-char message writes use bracketed paste regardless of
task state. Reverts the queuing approach (the TUI does accept input while
busy when delivered correctly).
Adds four MCP tools so Claude Code sessions can self-schedule:
- claudia_cron_create: schedule a recurring or one-shot prompt on any task
- claudia_cron_list: list all scheduled tasks (optionally filtered by taskId)
- claudia_cron_delete: cancel a scheduled task by cronId
- claudia_cron_pause: pause/resume a scheduled task

The orchestration system prompt is updated so each session knows its own
task ID and how to use cron expressions for periodic checks, delayed
follow-ups, or recurring monitoring.
gh pr list --head main returns unrelated PRs (e.g. from forks) that aren't
the workspace's own PR, so a workspace on main incorrectly showed a badge.
Now skip the PR lookup entirely when the branch matches the repo's default
branch and clear any stale prInfo.
The claudia MCP server needs per-task env vars (CLAUDIA_WORKSPACE_ID,
CLAUDIA_TASK_ID) to function. It was being included in the workspace
.mcp.json sync WITHOUT those vars, and Claude Code loaded that env-less
copy instead of the per-task --mcp-config copy -- causing "Claudia MCP
tools aren't available." Only inject the claudia server entry when a task
context (workspaceId) is present, so it only appears in --mcp-config.
Ovtcharov added 15 commits June 8, 2026 11:07
The PR badge <a> tag is natively draggable, which intercepted dragenter
events when dragging workspaces over a header containing a PR badge,
preventing the drop-target highlight. Set draggable={false} on the link.
PR info broadcasts were sending the entire workspaces array via
workspace:updated, which triggered setWorkspaces() on the frontend and
re-rendered the full workspace list. This killed in-progress workspace
drag-and-drop because the browser's native drag tracking lost its DOM
references. Now broadcast only the affected workspace (singular payload),
which uses updateWorkspace() — a targeted merge that doesn't re-render
unrelated workspace sections.
Inline worktree task items were draggable={true} with no-op drag handlers,
which let them initiate spurious drags and intercept dragenter events from
neighboring real task drags. Set draggable={false} when worktreeInfo is
present so they don't interfere with task reordering.
The system prompt now explicitly tells Claude to keep the title current
whenever the focus of work shifts (not just "if work evolves significantly").
The mid-session title instruction is injected every 5th follow-up message
instead of only once, reinforcing that stale titles should be updated.
Resumed sessions (--resume) don't load MCP servers from --mcp-config,
only from the workspace .mcp.json. The previous fix excluded claudia
from .mcp.json entirely, breaking MCP tools on resumed tasks. Now build
a per-workspace .mcp.json with CLAUDIA_WORKSPACE_ID set so the claudia
MCP server works on both new and resumed sessions. Also debounce the
worktree discovery trigger and cache non-git repos to reduce log spam.
The four claudia_cron_* tools were registered twice, causing the MCP
server to crash on startup with "Tool claudia_cron_create is already
registered". This silently broke ALL claudia MCP tools for every task
session — the root cause of the persistent "Claudia MCP tools aren't
available" reports.
Critical: parseWorktreeListOutput had isFirst inside the loop, so every
worktree got isMain=true. discoverWorktrees filtered !isMain which always
returned empty -- session worktree attribution never worked. Fixed by
setting isMain=false for all, then marking only results[0] as true.

Also: task:rename workspace broadcast uses singular update (not full array)
to avoid killing drag; removed double-decode in toPublicTask; clean up
prInfoCache on workspace delete.
- Auto-accept Claude Code permission prompts at PTY level when skip-
  permissions is enabled (bypasses known CLI bugs with
  --dangerously-skip-permissions)
- Pass --allowedTools * instead of mcp__* when skip-permissions is on
- Write allow: ['*'] to settings.local.json for all workspaces
  (including self) when skip-permissions is enabled
- Trigger workspace sync when skipPermissions config changes
- Merge regular tasks and worktree groups into a unified sorted list
  so new worktree tasks appear at their natural position
- Filter worktree child workspaces from references submenu
- Fix context menu z-index so it renders above sibling workspace cards
claudia_list_tasks and claudia_stop_all_tasks were filtering by exact
workspace ID match, which excluded tasks running in worktree child
workspaces. Now fetches the workspace list to find children with
matching worktreeParentId and includes their tasks in results.
- claudia_list_tasks now shows all tasks including disconnected and
  interrupted ones, with a canResume field indicating resumability
- claudia_continue_task explicitly documents support for disconnected/
  interrupted states (auto-reconnect was already handled by backend)
- claudia_get_task_status includes canResume field
- Updated tool descriptions to clarify cross-state visibility
Agents can now request task deletion via claudia_delete_task, but it
requires explicit user approval — a ConfirmModal appears in the UI and
the user must click "Delete" before the task is archived. If the user
clicks "Keep" or ignores the popup (60s timeout), the deletion is
rejected and the agent is notified.

The system prompt guidance explicitly states agents must never delete
tasks automatically — only when the user explicitly asks.
…-as-read

- New Inbox tab in the right-hand FileExplorer panel showing GitHub
  notifications via gh api notifications
- Unread count badge on the tab button (caps at 9+)
- Mark individual notifications as read with optimistic UI update
- Notification items show type icon, reason badge, clickable title,
  repo name, and relative timestamp
- Auto-refresh every 60 seconds when tab is active
- Backend endpoints: GET /api/github/notifications, PATCH .../threadId
- Extracted shared formatRelativeDate helper, fixed duplicate Issues tab
- Fixed apiUrlToHtmlUrl to not generate broken release URLs
- Fixed stale closure in markAsRead badge count via useEffect derivation
Add complete planning documentation for Claudia Manager MVP - an enhanced
task tracking and health monitoring system for Claudia Code.

Planning Summary:
- 13 iterations of comprehensive review
- 107 issues identified and resolved (24 CRITICAL, 22 HIGH, 42 MEDIUM, 19 LOW)
- 99% confidence level achieved
- $0/month operational cost for MVP

Core Documents:
- claudia-manager.md: Main specification (~4,000 lines)
- ABSOLUTELY-FINAL.md: Final certification and summary
- REVIEW-COMPLETE.md: Complete iteration history
- SCOPE-UPDATE-GITHUB-WEBHOOKS.md: Event-driven GitHub integration

Iteration Reviews:
- critique-v2.md: Initial architecture review
- red-team-review.md: Hostile security review
- implementation-reality.md: Code-level integration validation
- production-ops.md: Production operations review
- catastrophic-failures.md: Worst-case scenario analysis
- scale-load.md: Performance at scale (1000 tasks, 200 repos)
- security-audit.md: Defense-in-depth security review
- ux-edge-cases.md: User experience and accessibility
- api-compatibility.md: Backward compatibility and versioning
- deployment-ops.md: Deployment and rollback procedures
- testing-docs.md: Test strategy and documentation
- monitoring-final.md: Monitoring and alerting

MVP Features (3-week timeline):
1. Task Labels - Organize tasks with custom tags
2. Health Monitor - Detect stalled/errored tasks automatically
3. GitHub Integration - Event-driven webhooks for PR/issue tracking

Validation Criteria (Week 3):
- Label adoption ≥50%
- Filter usage ≥30%
- Health monitor clicks ≥5/day
- GitHub tasks ≥5/day
- User satisfaction ≥4/5

Next Steps:
- Week 0: Preparation (dependencies, security modules)
- Week 1-2: Implementation
- Week 3: Validation and go/no-go decision

See ABSOLUTELY-FINAL.md for executive summary and REVIEW-COMPLETE.md
for complete iteration history.
Tasks created via claudia_create_task now appear as collapsible
subtasks under the orchestrator task that spawned them. This makes
it easy to see which tasks belong to a multi-agent run without
hunting through collapsed worktree groups.

- Add parentTaskId field to Task type and PersistedTask (survives restart)
- MCP claudia_create_task sends SELF_TASK_ID as parentTaskId automatically
- Frontend groups same-workspace subtasks under their parent task row
- Nested worktrees (worktrees-within-worktrees) are flattened recursively
  and shown as indented subtasks under the orchestrator task row
- Collapsible subtask toggle shows count, active pulse, and chevron
- Remove the old WorktreeGroupSection header row — tasks render flat
…inal size fixes

Subtask improvements:
- Collapse/expand toggle rendered inline in the parent task row (pill badge with count)
- Sub-worktree tasks carry workspace-level worktreeBranch and prInfo through SubtaskEntry
- SubtaskList and TaskWithSubtasks wired to onRefreshTaskPr for hover-triggered refresh

PR badge refresh:
- task:select and new task:refreshPr (hover) trigger immediate backend PR lookup
- refreshPrInfoFor uses repo root (worktreeParentId || ws.id) — fixes wrong path for worktrees
- sessionWorktreePrInfo now re-fetched on the 30s interval (was set-once-and-frozen)
- Idle/interrupted tasks included in interval refresh so merged PRs show up post-completion
- user-triggered refreshes bypass prInfoInFlight guard so hover always fires

Bypass permissions fix:
- permissionMode=bypassPermissions now treated same as skipPermissions toggle
- Affects --dangerously-skip-permissions flag, --allowedTools *, and settings.local.json

Batch delete confirmation:
- Multiple claudia_delete_task MCP calls now queue into one modal with checkboxes
- Each task pre-checked; user can uncheck to keep; confirm button shows count

Notification fix:
- Replace undefined --background-secondary with solid var(--bg-tertiary) + color-mix tints

Terminal size fix:
- Replace pixel-math guess (clientWidth/9) with lastKnownTerminalSize from FitAddon
- New tasks spawn PTY at the real measured terminal size, eliminating narrow-width bug
Ovtcharov added 2 commits July 13, 2026 14:38
…ence

Resolved merge conflicts integrating:
- WebGL2 + Unicode 11 terminal rendering (#73)
- Per-task checkpoints with git snapshot/restore (#74)
- Persistent UI state across reconnect/reload (#79)
- Reconnect input delivery fix (#78)

All our branch features preserved:
- Subtask nesting and inline collapse toggle
- PR badge refresh on hover/click
- Batch delete confirmation modal
- Bypass permissions propagation fix
- Terminal size seeding from FitAddon
- Notification transparency fix
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.

2 participants