Skip to content

feat: task-based workspace names - #25

Merged
MRL-00 merged 4 commits into
mainfrom
mobile/hanoi
Aug 1, 2026
Merged

feat: task-based workspace names#25
MRL-00 merged 4 commits into
mainfrom
mobile/hanoi

Conversation

@MRL-00

@MRL-00 MRL-00 commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

Workspaces created from the phone no longer keep a random city name (Conductor-style) forever. They start with a temporary "New Workspace" label and are renamed from the task the user actually posts.

What changed

  • Server (server/server.ts) — new worktrees get the temporary label; on the first user message in a fresh workspace, a short task-derived name (markdown/code fences stripped, first line, ≤50 chars) is persisted via new setStoredName(). City id stays internal to the git worktree folder/branch. Added GET /workspaces/:id (with workspaceJSON refactor) so the app can re-read the name.
  • iOSChatView tracks workspaceName in @State and refreshes it after each turn (header flips to the task name); new APIClient.workspace(_:) method.
  • chore: bump build to 18 — separate commit, per repo release convention.

User impact

  • New workspace → "New Workspace" → after the first message, e.g. "Add a dark mode toggle to the settings screen".
  • Existing city-named workspaces that never received a message get renamed from their first task; already-active ones keep their city label.

Validation

  • Server: bun build passes (only unrelated runtime-installed qrcode-terminal dep flagged).
  • App: full archive + export + TestFlight upload succeeded (build 18 / 0.2.0, now processing on ASC).

Follow-ups

  • Naming is a local heuristic; an LLM-generated title or manual rename are easy additions if wanted.

Summary by CodeRabbit

  • New Features

    • New workspaces now begin with the label “New Workspace.”
    • Workspace names automatically update based on the first message’s task name.
    • Added workspace detail retrieval and improved workspace information refresh in chat.
    • Workspace creation now preserves recognizable workspace labels while retaining existing folder and branch naming.
  • Bug Fixes

    • Chat headers now reflect server-side workspace name changes after refreshing.

MRL-00 and others added 3 commits July 24, 2026 11:38
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New workspaces show a temporary "New Workspace" label until the first
user message, which the companion server turns into a short task-derived
name (markdown stripped, first line, capped at 50 chars). The chat header
and workspace list pick up the rename live via the new GET /workspaces/:id
endpoint; git worktree folders/branches keep their city id.
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@MRL-00, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d3199d6a-30c2-4080-a3bc-d1962294053d

📥 Commits

Reviewing files that changed from the base of the PR and between 433b800 and 5821e47.

📒 Files selected for processing (3)
  • PiMobile/APIClient.swift
  • PiMobile/Views/ChatView.swift
  • server/server.ts
📝 Walkthrough

Walkthrough

The server now assigns temporary workspace labels and replaces them with task-derived names after the first usable message. The iOS client retrieves updated workspace data and refreshes the chat header. The project build number changes from 12 to 18.

Changes

Workspace naming and synchronization

Layer / File(s) Summary
Server workspace labels
server/server.ts
New workspaces use "New Workspace". The first usable user message produces a Markdown-cleaned label, which is persisted in projects.json.
Workspace retrieval endpoint
server/server.ts
An authenticated GET /workspaces/{id} endpoint returns the current workspace representation or a 404 response.
iOS workspace header refresh
PiMobile/APIClient.swift, PiMobile/Views/ChatView.swift, PiMobile/Views/WorkspacesView.swift
The client retrieves a workspace during refresh and displays its current server-side name in the chat toolbar. The creation documentation describes the temporary label.

Release metadata

Layer / File(s) Summary
iOS build number update
PiMobile.xcodeproj/project.pbxproj
The Debug and Release build numbers change from 12 to 18.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChatView
  participant APIClient
  participant Server
  participant projects.json

  ChatView->>APIClient: Refresh workspace
  APIClient->>Server: GET /workspaces/{id}
  Server->>projects.json: Read current label
  Server-->>APIClient: Return workspaceJSON
  APIClient-->>ChatView: Update workspaceName
Loading

Possibly related PRs

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: task-based names for workspaces.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch mobile/hanoi
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mobile/hanoi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/server.ts`:
- Around line 699-713: The taskNameFrom truncation currently allows 50 content
characters plus the ellipsis; adjust its truncation budget so the complete
returned label, including the suffix, never exceeds 50 characters. Preserve the
existing word-boundary behavior and unchanged output for labels already within
the limit.
- Around line 769-774: Restrict the task-based renaming block around
taskNameFrom and setStoredName to phone-created workspaces only. Before
persisting the derived name, validate the workspace’s phone-worktree path or
explicit workspace origin, while preserving the existing first-message and
sessionless checks for eligible workspaces; do not rename the main checkout or
/projects folders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 54d03a00-85b6-48c1-bda4-e9e6615bdedf

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7e787 and 433b800.

📒 Files selected for processing (5)
  • PiMobile.xcodeproj/project.pbxproj
  • PiMobile/APIClient.swift
  • PiMobile/Views/ChatView.swift
  • PiMobile/Views/WorkspacesView.swift
  • server/server.ts

Comment thread server/server.ts
Comment on lines +699 to +713
const taskNameFrom = (text: string): string => {
// Drop fenced code blocks first, then take the first non-empty line.
const firstLine = text.replace(/```[\s\S]*?```/g, " ").split("\n").find((l) => l.trim()) ?? "";
const cleaned = firstLine
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1") // links keep their visible text
.replace(/[#>*_`~|]/g, " ")
.replace(/\s+/g, " ")
.trim();
if (!cleaned) return "";
const MAX = 50;
if (cleaned.length <= MAX) return cleaned;
const cut = cleaned.slice(0, MAX + 1);
const space = cut.lastIndexOf(" ");
return (space > MAX * 0.6 ? cut.slice(0, space) : cut.slice(0, MAX)).trimEnd() + "…";
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the complete workspace label within 50 characters.

For an overlong task, MAX limits the text before . The returned label can therefore contain 50 characters plus the ellipsis, for a total of 51 characters. Use a 49-character content budget when adding the suffix.

The PR objective requires a 50-character limit.

Proposed fix
   const MAX = 50;
+  const suffix = "…";
+  const contentMax = MAX - suffix.length;
-  if (cleaned.length <= MAX) return cleaned;
-  const cut = cleaned.slice(0, MAX + 1);
+  if (cleaned.length <= MAX) return cleaned;
+  const cut = cleaned.slice(0, contentMax + 1);
   const space = cut.lastIndexOf(" ");
-  return (space > MAX * 0.6 ? cut.slice(0, space) : cut.slice(0, MAX)).trimEnd() + "…";
+  return (space > contentMax * 0.6 ? cut.slice(0, space) : cut.slice(0, contentMax)).trimEnd() + suffix;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const taskNameFrom = (text: string): string => {
// Drop fenced code blocks first, then take the first non-empty line.
const firstLine = text.replace(/```[\s\S]*?```/g, " ").split("\n").find((l) => l.trim()) ?? "";
const cleaned = firstLine
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1") // links keep their visible text
.replace(/[#>*_`~|]/g, " ")
.replace(/\s+/g, " ")
.trim();
if (!cleaned) return "";
const MAX = 50;
if (cleaned.length <= MAX) return cleaned;
const cut = cleaned.slice(0, MAX + 1);
const space = cut.lastIndexOf(" ");
return (space > MAX * 0.6 ? cut.slice(0, space) : cut.slice(0, MAX)).trimEnd() + "…";
};
const taskNameFrom = (text: string): string => {
// Drop fenced code blocks first, then take the first non-empty line.
const firstLine = text.replace(/
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/server.ts` around lines 699 - 713, The taskNameFrom truncation
currently allows 50 content characters plus the ellipsis; adjust its truncation
budget so the complete returned label, including the suffix, never exceeds 50
characters. Preserve the existing word-boundary behavior and unchanged output
for labels already within the limit.

Comment thread server/server.ts
Comment on lines +769 to +774
// First message in a brand-new workspace: swap the temporary "New Workspace"
// label for a short name derived from the task itself.
if (!found && (scanned().workspaces.get(encodeCwd(cwd))?.sessionCount ?? 0) === 0) {
const name = taskNameFrom(text);
if (name) setStoredName(cwd, name);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Limit task-based renaming to phone-created workspaces.

This condition applies to every sessionless cwd. It includes the main checkout and folders registered through /projects. workspaceLabel states that those folders keep their folder names, but this block persists a task name for them after the first message.

Check the phone-worktree path, or use an explicit workspace origin, before calling setStoredName.

The PR objective scopes task-based names to phone-created workspaces.

Proposed fix
+  const isPhoneWorkspace = cwd.startsWith(`${homedir()}/pi-workspaces/`);
-  if (!found && (scanned().workspaces.get(encodeCwd(cwd))?.sessionCount ?? 0) === 0) {
+  if (
+    isPhoneWorkspace &&
+    !found &&
+    (scanned().workspaces.get(encodeCwd(cwd))?.sessionCount ?? 0) === 0
+  ) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// First message in a brand-new workspace: swap the temporary "New Workspace"
// label for a short name derived from the task itself.
if (!found && (scanned().workspaces.get(encodeCwd(cwd))?.sessionCount ?? 0) === 0) {
const name = taskNameFrom(text);
if (name) setStoredName(cwd, name);
}
// First message in a brand-new workspace: swap the temporary "New Workspace"
// label for a short name derived from the task itself.
const isPhoneWorkspace = cwd.startsWith(`${homedir()}/pi-workspaces/`);
if (
isPhoneWorkspace &&
!found &&
(scanned().workspaces.get(encodeCwd(cwd))?.sessionCount ?? 0) === 0
) {
const name = taskNameFrom(text);
if (name) setStoredName(cwd, name);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/server.ts` around lines 769 - 774, Restrict the task-based renaming
block around taskNameFrom and setStoredName to phone-created workspaces only.
Before persisting the derived name, validate the workspace’s phone-worktree path
or explicit workspace origin, while preserving the existing first-message and
sessionless checks for eligible workspaces; do not rename the main checkout or
/projects folders.

# Conflicts:
#	PiMobile.xcodeproj/project.pbxproj
@MRL-00
MRL-00 merged commit b169b74 into main Aug 1, 2026
1 check passed
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