Skip to content

Speed up task open paths#95

Open
jimmystridh wants to merge 2 commits into
debuglebowski:mainfrom
jimmystridh:codex/task-open-performance
Open

Speed up task open paths#95
jimmystridh wants to merge 2 commits into
debuglebowski:mainfrom
jimmystridh:codex/task-open-performance

Conversation

@jimmystridh
Copy link
Copy Markdown
Contributor

@jimmystridh jimmystridh commented May 29, 2026

Summary

Optimizes opening a task from the sidebar by:

  • Prime the task detail Suspense cache from sidebar and board state before opening a known task.
  • Reuse task and project lookup maps instead of repeatedly scanning arrays on task-open paths.
  • Reduce avoidable task, kanban, project select, and terminal re-renders in related open/selection flows.
  • Add focused coverage for Suspense cache priming and closed-task sidebar open performance.

Root Cause

Opening a task from the sidebar could still suspend on task detail data even when the current app state already had enough task, project, panel, and browser-tab data to render the detail page. Some adjacent open paths also rebuilt lookup state or re-rendered more work than needed.

Fix Details

  • Added SuspenseCache.prime(...) so known resolved data can enter the cache without throwing a pending promise.
  • Added task detail snapshot builders that mirror the async fetch shape, then prime the cache before task open.
  • Prefetch task detail on sidebar row hover, focus, mousedown, and click.
  • Optimized tab insertion and selection helpers with maps and memoized state.
  • Added a focused Playwright perf scenario for opening a closed task from the sidebar.

Performance Numbers Seen

Focused scenario: open-closed-task-from-sidebar.

Before:

  • p50: 676 ms
  • p95: 1176 ms
  • Detail loading shell commonly waited around 400 ms before render

After:

  • p50: 313 ms
  • p95: 391 ms
  • Task detail mount mark observed around 100-190 ms
  • Known sidebar tasks no longer showed the Suspense shell before detail render

Validation

In the fresh worktree/branch:

  • pnpm install --frozen-lockfile
  • pnpm --filter @slayzone/app typecheck
  • pnpm --filter @slayzone/app exec vitest run --root ../../.. packages/shared/suspense/src/suspense-cache.test.tsx packages/domains/task/src/client/taskDetailCache.test.ts

Earlier validation from the source branch:

  • npx playwright test --config playwright.config.ts e2e/perf/scenarios.spec.ts -g open-closed-task-from-sidebar
  • pnpm build:mac

CI Note

The initial PR run failed in check-windows inside packages/shared/platform, not in the performance code. Latest main had the same check-windows platform failures on May 23, 2026. This PR includes a small follow-up fix to make those platform shell tests portable on Windows:

  • Use the target platform path separator when computing CLI install paths under mocked platforms.
  • Simulate migration rename failure directly instead of relying on chmod-based unwritable directories, which do not behave the same on Windows.

Greptile Summary

This PR speeds up task-opening paths by priming task detail data and reducing repeated lookup work. The main changes are:

  • Added Suspense cache priming for known task detail snapshots.
  • Prefetched task detail data from sidebar row hover, focus, mouse down, and click paths.
  • Reused task and project lookup maps across task open and rendering flows.
  • Reduced terminal state subscriptions in kanban card and list views.
  • Added a focused perf scenario and adjusted platform tests for Windows portability.

Confidence Score: 3/5

These issues should be fixed before merging.

  • Opening a task from another project can briefly render with the wrong project-path status.
  • Kanban and list views can stop showing terminal error or stopped states.
  • The create task dialog no longer self-loads projects when used without an injected list.

Focus on App.tsx, PtyContext.tsx, and CreateTaskDialog.tsx.

Important Files Changed

Filename Overview
packages/apps/app/src/renderer/src/App.tsx Adds task detail snapshot priming and lookup maps, with a wrong project-path snapshot for cross-project opens.
packages/domains/terminal/src/client/PtyContext.tsx Adds a narrower terminal-state hook that hides non-alive terminal states from kanban/list consumers.
packages/domains/task/src/client/CreateTaskDialog.tsx Switches project options to injected data, changing standalone dialog behavior when no projects prop is supplied.

Comments Outside Diff (1)

  1. packages/domains/task/src/client/CreateTaskDialog.tsx, line 39-48 (link)

    P2 Standalone project list breaks projects now defaults to an empty array, and the dialog always passes that array into ProjectSelect. Since ProjectSelect treats any provided array as controlled and skips loading from the DB, any caller that omits projects gets an empty project dropdown instead of the previous self-loading behavior.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/domains/task/src/client/CreateTaskDialog.tsx
    Line: 39-48
    
    Comment:
    **Standalone project list breaks** `projects` now defaults to an empty array, and the dialog always passes that array into `ProjectSelect`. Since `ProjectSelect` treats any provided array as controlled and skips loading from the DB, any caller that omits `projects` gets an empty project dropdown instead of the previous self-loading behavior.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/apps/app/src/renderer/src/App.tsx:671-678
**Wrong path status** This snapshot only carries the cached `projectPathMissing` value when the opened task belongs to the currently selected project. The sidebar can open tasks from other projects, so a task whose own project path is missing gets primed with `projectPathMissing: false` and the detail page renders repo-dependent UI before its async recheck corrects the state.

### Issue 2 of 3
packages/domains/terminal/src/client/PtyContext.tsx:629-646
**Terminal errors disappear** This hook only returns a state while `activeTaskIds` contains the task, but that set only tracks alive terminals (`running` and `idle`). When a terminal enters `error` or `dead`, kanban cards and list rows now receive `undefined` and hide the red/stopped indicator that the previous direct subscription could display.

### Issue 3 of 3
packages/domains/task/src/client/CreateTaskDialog.tsx:39-48
**Standalone project list breaks** `projects` now defaults to an empty array, and the dialog always passes that array into `ProjectSelect`. Since `ProjectSelect` treats any provided array as controlled and skips loading from the DB, any caller that omits `projects` gets an empty project dropdown instead of the previous self-loading behavior.

```suggestion
export function CreateTaskDialog({
  open,
  onOpenChange,
  onCreated,
  onCreatedAndOpen,
  draft,
  tags,
  projects,
  onTagCreated
}: CreateTaskDialogProps): React.JSX.Element {
```

Reviews (1): Last reviewed commit: "fix(platform): make shell tests portable" | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@jimmystridh
Copy link
Copy Markdown
Contributor Author

jimmystridh commented May 29, 2026

CI fix note: the red check-windows job was not caused by the task-open performance changes. It was an inherited Windows portability failure in packages/shared/platform (mocked macOS paths using Windows separators, plus chmod-based unwritable-dir assumptions on Windows). Latest main already showed the same platform failures on May 23, 2026.

I added a focused follow-up fix here: cf083f0

After that commit, PR checks pass: check, check-windows, and workflow-lint.

@jimmystridh jimmystridh marked this pull request as ready for review May 29, 2026 14:58
Comment on lines +671 to +678
buildTaskDetailDataFromSnapshot({
task,
tasks,
projects,
tags,
taskTagIds: taskTags.get(taskId) ?? [],
projectPathMissing: task.project_id === selectedProjectId ? projectPathMissing : false
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Wrong path status This snapshot only carries the cached projectPathMissing value when the opened task belongs to the currently selected project. The sidebar can open tasks from other projects, so a task whose own project path is missing gets primed with projectPathMissing: false and the detail page renders repo-dependent UI before its async recheck corrects the state.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/apps/app/src/renderer/src/App.tsx
Line: 671-678

Comment:
**Wrong path status** This snapshot only carries the cached `projectPathMissing` value when the opened task belongs to the currently selected project. The sidebar can open tasks from other projects, so a task whose own project path is missing gets primed with `projectPathMissing: false` and the detail page renders repo-dependent UI before its async recheck corrects the state.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +629 to +646
export function useKnownTaskTerminalState(taskId: string): TerminalState | undefined {
const activeTaskIds = useActiveTaskIds()
const shouldSubscribe = activeTaskIds.has(taskId)
const sessionId = `${taskId}:${taskId}`
const { getState, subscribeState } = usePty()
const [terminalState, setTerminalState] = useState<TerminalState | undefined>(() =>
shouldSubscribe ? getState(sessionId) : undefined
)

useEffect(() => {
if (!shouldSubscribe) {
setTerminalState(undefined)
return
}

setTerminalState(getState(sessionId))
return subscribeState(sessionId, (newState) => setTerminalState(newState))
}, [shouldSubscribe, sessionId, getState, subscribeState])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Terminal errors disappear This hook only returns a state while activeTaskIds contains the task, but that set only tracks alive terminals (running and idle). When a terminal enters error or dead, kanban cards and list rows now receive undefined and hide the red/stopped indicator that the previous direct subscription could display.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/domains/terminal/src/client/PtyContext.tsx
Line: 629-646

Comment:
**Terminal errors disappear** This hook only returns a state while `activeTaskIds` contains the task, but that set only tracks alive terminals (`running` and `idle`). When a terminal enters `error` or `dead`, kanban cards and list rows now receive `undefined` and hide the red/stopped indicator that the previous direct subscription could display.

How can I resolve this? If you propose a fix, please make it concise.

@jimmystridh jimmystridh changed the title [codex] Speed up task open paths Speed up task open paths May 29, 2026
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