WH-007: Default project selection to most recently used#67
WH-007: Default project selection to most recently used#67
Conversation
… route Both branches added exports to worktree.ts (getPendingChanges from HEAD, readWorktreeFile/deleteWorktreeFile from wh-007-spec). Resolved by keeping all three imports. https://claude.ai/code/session_01Udmw8qhbhJBQ1dQ1CgRk7g
|
🦸 Review Hero Summary Below consensus threshold (3 unique issues not confirmed by majority)
Nitpicks
Local fix prompt (copy to your coding agent)Fix these issues identified on the pull request. One commit per issue fixed.
|
| const wtPath = worktreePath(owner, repo, cardIdentifier) | ||
| const fullPath = safeResolvePath(wtPath, filePath) | ||
| try { | ||
| await fs.unlink(fullPath) |
There was a problem hiding this comment.
[Bugs & Correctness] suggestion
The catch block in deleteWorktreeFile silently swallows all errors, not just ENOENT (file not found). Permissions errors, I/O errors, or path traversal violations thrown by safeResolvePath would all be silently ignored. Fix: only ignore ENOENT — catch (err) { if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err }
| const cookieStore = await cookies() | ||
| const lastSlug = cookieStore.get('workhorse_last_project')?.value | ||
| const lastProject = lastSlug | ||
| ? projects.find((p) => p.name.toLowerCase() === lastSlug) |
There was a problem hiding this comment.
[Bugs & Correctness] critical
Cookie value is URI-encoded when written (Sidebar.tsx:69 uses encodeURIComponent(activeProject.name.toLowerCase())), but compared without decoding here. For any project name containing spaces or non-ASCII characters, lastSlug will be something like my%20project while p.name.toLowerCase() returns my project, so lastProject will always be null and the feature silently falls back to projects[0]. Fix: decode the cookie value before comparing — decodeURIComponent(lastSlug) — or store it unencoded.
| const handleCloseModal = useCallback(() => setCreateModal(null), []) | ||
|
|
||
| // Persist the active project so the home page can redirect back to it | ||
| useEffect(() => { |
There was a problem hiding this comment.
[Security] suggestion
The cookie is set without the Secure flag: document.cookie = 'workhorse_last_project=...; path=/; max-age=31536000; SameSite=Lax'. Without Secure, the browser will send this cookie over plain HTTP connections, making it visible to network observers. While the value is just a project slug (low sensitivity), it is also read server-side during redirects — an attacker on the same network could silently swap it to redirect the user to a different project. Add ; Secure to restrict transmission to HTTPS only.
| const prSignal = await readWorktreeFile(ctx.owner, ctx.repoName, ctx.card.identifier, '.workhorse/pr-request.json') | ||
| if (prSignal) { | ||
| const { title, body } = JSON.parse(prSignal) as { title: string; body: string } | ||
| const branchName = ctx.card.cardBranch |
There was a problem hiding this comment.
[Security] suggestion
JSON.parse(prSignal) is called on content written by the AI agent to a file on disk. There is no schema validation — the destructured title and body could be non-string types (e.g., objects, arrays) that stringify in unexpected ways when forwarded to the GitHub API. More critically, if the agent produces a crafted payload with a very long title (GitHub enforces a 256-character limit and will reject it with a 422), the error is silently swallowed by the outer catch, leaving the card in a state where prNumber/prUrl are never set and the signal file is never deleted — meaning the next session will re-attempt PR creation. Validate that title and body are non-empty strings (and optionally enforce a sane title length) before calling createPullRequest.
|
🦸 Review Hero Summary Below consensus threshold (5 unique issues not confirmed by majority)
Local fix prompt (copy to your coding agent)Fix these issues identified on the pull request. One commit per issue fixed.
|
Summary
When a user loads Workhorse, the project selector defaults to the most recently selected project for that user. This ensures users are returned to their last working context without needing to reselect manually.
Journey
Specs
.workhorse/specs/product-navigation.mdChanged files
src/app/(main)/page.tsxsrc/components/Sidebar.tsxsrc/app/api/agent-session/route.tssrc/lib/git/worktree.tsTest plan
Review Hero