Skip to content

feat(desktop): v2 AI workspace rename generates title + branch together#3692

Merged
saddlepaddle merged 6 commits into
mainfrom
boiling-silver
Apr 24, 2026
Merged

feat(desktop): v2 AI workspace rename generates title + branch together#3692
saddlepaddle merged 6 commits into
mainfrom
boiling-silver

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Apr 23, 2026

Summary

  • Post-create AI rename on v2 workspaces now makes a single structured-output call (Mastra structuredOutput + zod schema) returning { title, branchName } instead of two separate text-gen calls with a naive slice(0, 20) that was producing mid-word truncations like "New v2 workspaces na".
  • Title is applied to v2_workspaces.name via the existing updateNameFromHost mutation (now extended to accept optional branch alongside optional name).
  • The git branch is also renamed now: git branch -m inside the worktree, then host-service/workspaces.branch + v2_workspaces.branch both updated. Dedup still runs against fresh branches in case of collision.
  • Auth/credentials path is unchanged — uses getSmallModel() so the user's Claude subscription is hit the same way as generateTitleFromMessage. jsonPromptInjection: true keeps it working over OAuth.
  • Branch-prefix support for v2 is still missing entirely (no v2 prefix schema, no reader, no UI). Tracked separately in SUPER-478.

Test plan

  • Create a new v2 workspace with a descriptive prompt and no user-typed title — verify the workspace name is a clean, full-word title (not mid-word truncated) and the git branch is renamed from friendlyFallback to a kebab-case slug.
  • Create a v2 workspace with a user-typed title — verify neither the title nor the branch is overridden by the AI rename.
  • Create a v2 workspace with no prompt — verify the friendly fallback is kept unchanged.
  • Force a branch name collision (same slug already exists) — verify deduplicateBranchName appends a suffix.
  • Trigger the rename while the worktree branch is checked out elsewhere — verify the git branch -m failure path logs a warning and the title still applies.
  • Sign in with Claude OAuth (no API key) — verify structured output still produces a usable { title, branchName }.

Summary by cubic

Generates both the v2 workspace title and git branch from the create prompt in one structured-output call and applies them together. Refactors the post-create rename into applyAiWorkspaceRename with a shared listBranchNames util for simpler, more reliable updates.

  • New Features

    • Single call returns { title, branchName } (zod-transformed). Title ≤150 chars with whole words; branch is kebab-case ≤25 chars, sanitized and deduped.
    • Applies both: git rename, then cloud via v2Workspace.updateNameFromHost; host-local branch only after cloud success.
    • v2Workspace.updateNameFromHost now accepts optional name and/or branch with expectedCurrentName (requires at least one).
    • Uses Agent.generate with structured output; adds @mastra/core. getSmallModel() returns MastraModelConfig | null.
    • Branch-prefix support remains out of scope (SUPER-478).
  • Bug Fixes

    • Roll back git branch rename if the cloud write fails to keep git, cloud, and local in sync.
    • Coerce oversized/invalid model outputs via schema transforms (trim/sanitize). If the branch sanitizes to empty, skip the branch rename.

Written for commit 920d7c2. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • AI workspace naming now returns both an optimized display title and a suggested branch name in one step.
  • Improvements

    • Workspace renames apply display and branch updates more reliably, with local branch handling, deduplication, and rollback if cloud synchronization fails.

Post-create rename now makes a single structured-output call returning
{ title, branchName }, applies the title to v2_workspaces.name, and also
renames the git branch (git branch -m in the worktree + updates the
host-local workspaces.branch + cloud v2_workspaces.branch). Replaces the
naive 20-char slice that was producing mid-word truncations like
"New v2 workspaces na". Branch-prefix support for v2 tracked in SUPER-478.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b6eaf2e5-cb3f-4d7d-8794-370027e6b1c5

📥 Commits

Reviewing files that changed from the base of the PR and between 9876d09 and 920d7c2.

📒 Files selected for processing (3)
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
  • packages/host-service/src/trpc/router/workspace-creation/utils/list-branch-names.ts
  • packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts

📝 Walkthrough

Walkthrough

Replaces a single-output workspace-naming helper with a new dual-output LLM flow that returns both display title and kebab-case branch name, validates and formats outputs, performs optional local git branch renames (with dedupe and rollback), and patches the cloud workspace and local DB accordingly.

Changes

Cohort / File(s) Summary
AI workspace naming (removed → added)
packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-name.ts (removed), packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
Deleted old single-output generator. Added generateWorkspaceNamesFromPrompt (returns `{ title, branchName }
Workspace creation orchestration
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Switched to calling applyAiWorkspaceRename (fire-and-forget) that can update title and/or branch; passes repo/worktree paths and old branch to enable local git operations.
V2 workspace router (API input/logic)
packages/trpc/src/router/v2-workspace/v2-workspace.ts
updateNameFromHost input now accepts optional name and optional branch with Zod refinement requiring at least one. Update logic builds a conditional patch and updates only provided fields while preserving expectedCurrentName check.
Small-model typing
packages/chat/src/server/shared/small-model/get-small-model.ts
getSmallModel() signature tightened to `Promise<MastraModelConfig
Branch listing helper
packages/host-service/src/trpc/router/workspace-creation/utils/list-branch-names.ts
Added listBranchNames to enumerate and deduplicate local and origin branch refs via git for-each-ref.
Dependencies
packages/host-service/package.json
Added runtime dependency @mastra/core@1.26.0-alpha.3.

Sequence Diagram

sequenceDiagram
    participant CreationRouter as Workspace Creation
    participant LLMClient as LLM Client
    participant GitLocal as Git (Local)
    participant CloudAPI as Cloud Workspace API
    participant LocalDB as Local Database

    CreationRouter->>LLMClient: generateWorkspaceNamesFromPrompt(prompt)
    LLMClient->>LLMClient: Load small model & call structured-output
    LLMClient->>LLMClient: Zod validate + format { title, branchName }
    LLMClient-->>CreationRouter: { title, branchName } or null

    alt Title changed
        CreationRouter->>CloudAPI: updateNameFromHost({ name, expectedCurrentName })
    end

    alt Branch changed
        CreationRouter->>CreationRouter: Deduplicate branch name (listBranchNames)
        CreationRouter->>GitLocal: git branch -m oldName newName
        GitLocal-->>CreationRouter: Success/Failure

        alt Git rename succeeded
            CreationRouter->>CloudAPI: updateNameFromHost({ branch })
            alt Cloud API fails
                CreationRouter->>GitLocal: git branch -m newName oldName (rollback)
            else Cloud API succeeds
                CreationRouter->>LocalDB: Update workspaces.branch (persist)
            end
        else Git rename failed
            CreationRouter-->>CreationRouter: Skip cloud/DB update
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nudged the model, whispered name and branch,

it hopped back titles trimmed and dashed with a blanched!
With zips and rolls and a careful git song,
we rename, patch, and persist — hop on, all day long! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: v2 AI workspace rename now generates both title and branch together using structured output, which is the core objective of this PR.
Description check ✅ Passed The PR description comprehensively covers the change summary, test plan, and implementation details, aligning well with the repository template's expected sections.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch boiling-silver

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR replaces two sequential text-generation calls (with naive slice(0,20) truncation) with a single Mastra structuredOutput call returning { title, branchName }, and extends updateNameFromHost to optionally update both v2_workspaces.name and v2_workspaces.branch in one mutation.

  • The fire-and-forget branch rename path commits git branch -m and the local workspaces.branch update inside a try/catch, then calls updateNameFromHost.mutate afterwards. If mutate throws, the outer .catch swallows the error and v2_workspaces.branch is permanently stale while git and the local table already reflect the new name.

Confidence Score: 4/5

Safe to merge once the partial-commit window in the branch rename path is addressed

One P1 finding: git + local-DB branch rename can succeed while v2_workspaces.branch silently fails to update, leaving cloud state permanently out of sync. The P2 sanitizer-ordering issue is low-probability in practice but worth fixing before the next iteration.

packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts (partial-commit window); packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts (sanitizer ordering)

Important Files Changed

Filename Overview
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts Integrates the new structured AI rename; contains a P1 partial-commit window where git + local-DB branch rename can succeed while the cloud v2_workspaces.branch update silently fails
packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts New file; schema and generation logic look solid, but sanitizeBranchCandidate runs after Zod validation so it can't rescue over-length model responses (silent rename no-op)
packages/chat/src/server/desktop/title-generation/structured-generation.ts New generic structured-output helper; dynamic import pattern, safe-parse double-validation, and null propagation all look correct
packages/trpc/src/router/v2-workspace/v2-workspace.ts updateNameFromHost now accepts optional name+branch with a .refine guard; conditional WHERE and patch building are correct
packages/chat/src/server/desktop/title-generation/index.ts Trivial re-export of the new generateObjectFromMessage; no issues
packages/chat/src/server/desktop/index.ts Public-API re-export updated to include generateObjectFromMessage; no issues
packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-name.ts Deleted file; replaced by ai-workspace-names.ts — removal is intentional and clean

Sequence Diagram

sequenceDiagram
    participant WC as workspace-creation.ts
    participant LLM as generateWorkspaceNamesFromPrompt
    participant Git as git (worktree)
    participant LocalDB as workspaces (local DB)
    participant Cloud as v2Workspace.updateNameFromHost

    WC->>LLM: generateWorkspaceNamesFromPrompt(prompt)
    LLM-->>WC: { title, branchName }

    WC->>WC: deduplicateBranchName(branchName, freshBranches)

    WC->>Git: git branch -m oldBranch deduped
    Git-->>WC: success
    WC->>LocalDB: UPDATE workspaces SET branch=deduped
    LocalDB-->>WC: ok

    WC->>Cloud: updateNameFromHost({ id, name?, branch? })
    alt mutate succeeds
        Cloud-->>WC: updated row
    else mutate fails (P1 gap)
        Cloud-->>WC: throws
        WC->>WC: outer .catch logs warning
        Note over Git,Cloud: git + localDB renamed,<br/>v2_workspaces.branch still stale
    end
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Line: 1034-1067

Comment:
**Git/local-DB rename can commit while `v2_workspaces.branch` stays stale**

The `git branch -m` and `ctx.db.update(workspaces)` at lines 1036–1041 run inside their own `try/catch`, so they commit independently. The subsequent `updateNameFromHost.mutate(patch)` at line 1067 can still throw (network error, workspace already deleted, JWT expiry, etc.), and that error is swallowed by the outer `.catch` at line 1069. At that point the local git branch and `workspaces.branch` carry the new name, but `v2_workspaces.branch` remains the original value — leaving the cloud record permanently out of sync with git.

In the old single-step code, a `mutate` failure was a soft skip; nothing had been mutated locally yet. This PR introduces a new partial-commit window that the existing catch path doesn't roll back.

Consider moving the local DB update (`ctx.db.update(workspaces)…`) to after the `mutate` succeeds, or — at minimum — adding a rollback `git branch -m deduped oldBranchName` in the catch block.

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

---

This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
Line: 86-93

Comment:
**`sanitizeBranchCandidate` is unreachable for over-length model responses**

`generateObjectFromMessage` runs `schema.safeParse(object)` and returns `null` when it fails. Because `workspaceNamesSchema` enforces `.max(BRANCH_NAME_MAX)` (25 chars), any model response where `branchName` is 26+ characters causes `safeParse` to fail and `result` is `null` — so `sanitizeBranchCandidate` (which has `.slice(0, BRANCH_NAME_MAX)`) never executes for those cases. The entire AI rename silently no-ops.

The doc comment acknowledges this for `title`, but the branch name has a much tighter ceiling (25 vs 40 chars), making silent misses more likely in practice. The simplest fix is to apply `sanitizeBranchCandidate` before calling `generateObjectFromMessage`, or use a Zod `.transform()` on the `branchName` field so sanitization happens inside the schema validation step.

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

Reviews (1): Last reviewed commit: "feat(desktop): v2 AI workspace rename ge..." | Re-trigger Greptile

Comment on lines +1034 to +1067
try {
const worktreeGit = await ctx.git(worktreePath);
await worktreeGit.raw(["branch", "-m", oldBranchName, deduped]);
ctx.db
.update(workspaces)
.set({ branch: deduped })
.where(eq(workspaces.id, cloudRow.id))
.run();
nextBranchName = deduped;
} catch (err) {
console.warn(
"[workspaceCreation.create] git branch rename failed",
err,
);
}
}

const patch: {
id: string;
name?: string;
branch?: string;
expectedCurrentName?: string;
} = { id: cloudRow.id };
if (aiNames.title && aiNames.title !== oldWorkspaceName) {
patch.name = aiNames.title;
patch.expectedCurrentName = oldWorkspaceName;
}
if (nextBranchName !== oldBranchName) {
patch.branch = nextBranchName;
}
if (patch.name === undefined && patch.branch === undefined) {
return;
}
await ctx.api.v2Workspace.updateNameFromHost.mutate(patch);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Git/local-DB rename can commit while v2_workspaces.branch stays stale

The git branch -m and ctx.db.update(workspaces) at lines 1036–1041 run inside their own try/catch, so they commit independently. The subsequent updateNameFromHost.mutate(patch) at line 1067 can still throw (network error, workspace already deleted, JWT expiry, etc.), and that error is swallowed by the outer .catch at line 1069. At that point the local git branch and workspaces.branch carry the new name, but v2_workspaces.branch remains the original value — leaving the cloud record permanently out of sync with git.

In the old single-step code, a mutate failure was a soft skip; nothing had been mutated locally yet. This PR introduces a new partial-commit window that the existing catch path doesn't roll back.

Consider moving the local DB update (ctx.db.update(workspaces)…) to after the mutate succeeds, or — at minimum — adding a rollback git branch -m deduped oldBranchName in the catch block.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
Line: 1034-1067

Comment:
**Git/local-DB rename can commit while `v2_workspaces.branch` stays stale**

The `git branch -m` and `ctx.db.update(workspaces)` at lines 1036–1041 run inside their own `try/catch`, so they commit independently. The subsequent `updateNameFromHost.mutate(patch)` at line 1067 can still throw (network error, workspace already deleted, JWT expiry, etc.), and that error is swallowed by the outer `.catch` at line 1069. At that point the local git branch and `workspaces.branch` carry the new name, but `v2_workspaces.branch` remains the original value — leaving the cloud record permanently out of sync with git.

In the old single-step code, a `mutate` failure was a soft skip; nothing had been mutated locally yet. This PR introduces a new partial-commit window that the existing catch path doesn't roll back.

Consider moving the local DB update (`ctx.db.update(workspaces)…`) to after the `mutate` succeeds, or — at minimum — adding a rollback `git branch -m deduped oldBranchName` in the catch block.

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

Comment on lines +86 to +93
if (!result) return null;

const branchName = sanitizeBranchCandidate(result.branchName);
if (!branchName) return null;
const title = result.title.trim();
if (!title) return null;

return { title, branchName };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 sanitizeBranchCandidate is unreachable for over-length model responses

generateObjectFromMessage runs schema.safeParse(object) and returns null when it fails. Because workspaceNamesSchema enforces .max(BRANCH_NAME_MAX) (25 chars), any model response where branchName is 26+ characters causes safeParse to fail and result is null — so sanitizeBranchCandidate (which has .slice(0, BRANCH_NAME_MAX)) never executes for those cases. The entire AI rename silently no-ops.

The doc comment acknowledges this for title, but the branch name has a much tighter ceiling (25 vs 40 chars), making silent misses more likely in practice. The simplest fix is to apply sanitizeBranchCandidate before calling generateObjectFromMessage, or use a Zod .transform() on the branchName field so sanitization happens inside the schema validation step.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
Line: 86-93

Comment:
**`sanitizeBranchCandidate` is unreachable for over-length model responses**

`generateObjectFromMessage` runs `schema.safeParse(object)` and returns `null` when it fails. Because `workspaceNamesSchema` enforces `.max(BRANCH_NAME_MAX)` (25 chars), any model response where `branchName` is 26+ characters causes `safeParse` to fail and `result` is `null` — so `sanitizeBranchCandidate` (which has `.slice(0, BRANCH_NAME_MAX)`) never executes for those cases. The entire AI rename silently no-ops.

The doc comment acknowledges this for `title`, but the branch name has a much tighter ceiling (25 vs 40 chars), making silent misses more likely in practice. The simplest fix is to apply `sanitizeBranchCandidate` before calling `generateObjectFromMessage`, or use a Zod `.transform()` on the `branchName` field so sanitization happens inside the schema validation step.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/chat/src/server/desktop/title-generation/structured-generation.ts (1)

45-51: Nit: consider narrowing the fallback if Agent is missing.

The string-variable dynamic import (const agentModuleId = "@mastra/core/agent") defeats static resolution on purpose, but the throw new Error(...) here converts a missing optional dep into a generic 500 at the caller. Since generateWorkspaceNamesFromPrompt already treats null as "skip rename", returning null here instead of throwing would keep the rename best-effort end-to-end and avoid noisy .catch logs in the happy-path-with-no-model case.

♻️ Proposed tweak
-	if (!Agent) {
-		throw new Error("Mastra Agent constructor is unavailable");
-	}
+	if (!Agent) {
+		return null;
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/chat/src/server/desktop/title-generation/structured-generation.ts`
around lines 45 - 51, The current dynamic import block throws a generic Error if
Agent is missing which turns an optional dependency into a hard failure;
instead, in the context of generateWorkspaceNamesFromPrompt keep the operation
best-effort by returning null when Agent is not available. Locate the dynamic
import using agentModuleId and the Agent symbol (Agent?: StructuredAgentCtor) in
structured-generation.ts and replace the throw new Error("Mastra Agent
constructor is unavailable") with a return null (or otherwise ensure the
surrounding generateWorkspaceNamesFromPrompt returns null) so the caller treats
this as a skip-rename case rather than a 500.
packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts (1)

17-24: branchName max(25) may reject valid model outputs frequently.

The prompt asks for "2-4 words" with only [a-z0-9-], but 2-4 meaningful English words commonly exceed 25 chars once dash-joined (e.g. implement-user-auth-flow = 24 is borderline; add-workspace-rename-feature = 28 fails). When zod rejects, generateObjectFromMessage returns null → rename is silently skipped and the user keeps the random friendly fallback.

Since sanitizeBranchCandidate already truncates to BRANCH_NAME_MAX downstream, consider letting the schema be more lenient (e.g. .max(60)) and letting the sanitizer enforce the hard cap. That way an over-length but otherwise correct suggestion still produces a usable branch name instead of being dropped entirely.

♻️ Proposed tweak
 	branchName: z
 		.string()
 		.trim()
 		.min(1)
-		.max(BRANCH_NAME_MAX)
+		.max(BRANCH_NAME_MAX * 2)
 		.describe(
 			`Git branch name in kebab-case (lowercase, dashes). 2-4 words, up to ${BRANCH_NAME_MAX} characters. Only [a-z0-9-]. No leading/trailing dashes. No prefixes.`,
 		),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`
around lines 17 - 24, The branchName zod rule is too strict (max set to
BRANCH_NAME_MAX/~25) causing valid AI outputs to be rejected and
generateObjectFromMessage to return null; relax the schema by increasing the max
(e.g. `.max(60)`) on branchName instead of the current BRANCH_NAME_MAX so longer
but valid kebab-case suggestions pass validation, then rely on
sanitizeBranchCandidate to truncate to BRANCH_NAME_MAX (the real hard cap)
before use; update the branchName.describe text if needed and keep references to
branchName, BRANCH_NAME_MAX, sanitizeBranchCandidate, and
generateObjectFromMessage when making the change.
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts (1)

1018-1049: Dedupe against oldBranchName-excluded list is correct — nice.

Filtering oldBranchName out of freshBranches before deduplicateBranchName avoids the footgun where the old (still-existing) branch would otherwise force an unnecessary suffix on the AI suggestion. And the outer if (aiNames.branchName && aiNames.branchName !== oldBranchName) short-circuits the degenerate case where the model re-produces the existing name.

One minor note: after git branch -m old new succeeds, the worktree directory on disk stays at .worktrees/<oldBranchName>. Functionally fine (git tracks refs, not paths), but the fs layout becomes misleading. If .worktrees/<oldBranchName>/ later ends up as the target for another workspace whose sanitized branch happens to equal oldBranchName, safeResolveWorktreePath will collide with an existing dir and git worktree add will fail with a less obvious error than a branch-ref collision. Not a blocker — just worth capturing in the SUPER-478 follow-up or a TODO.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`
around lines 1018 - 1049, After successfully renaming the branch in
generateWorkspaceNamesFromPrompt's code path (the block that calls
ctx.git(worktreePath).raw(["branch","-m", oldBranchName, deduped])), also rename
the on-disk worktree directory from the old sanitized name to the new one so the
.worktrees/<branchName> layout matches the branch ref; do this by resolving the
current worktreePath (used to create the worktree earlier), compute the new
worktree path for deduped (use the same sanitization/safeResolveWorktreePath
logic as when creating worktrees), check for collisions (fs.existsSync or
equivalent) and then fs.rename/move the directory, and handle errors (log and
fall back) so future safeResolveWorktreePath/git worktree add won't hit
misleading collisions. Ensure you reference worktreePath, oldBranchName,
deduped, ctx.git(...) and deduplicateBranchName in the change so reviewers can
find the spot.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`:
- Around line 1057-1067: The branch update lacks a concurrency guard similar to
expectedCurrentName, so add an expectedCurrentBranch check: when nextBranchName
!== oldBranchName set patch.branch = nextBranchName and also set
patch.expectedCurrentBranch = oldBranchName before calling
ctx.api.v2Workspace.updateNameFromHost.mutate(patch); if the server API doesn't
yet accept expectedCurrentBranch, either add that parameter on the server
handler or include a clear comment in workspace-creation.ts (near the
patch/branch logic) explaining why the asymmetry is intentional.
- Around line 1034-1067: The current sequence renames the git branch and updates
the local workspaces.row (variables/functions: worktreeGit via ctx.git,
workspaces, branch/deduped, nextBranchName, oldBranchName) before calling
ctx.api.v2Workspace.updateNameFromHost.mutate, which can leave local sqlite and
git diverged from cloud on mutation failure; fix by either (A) moving the cloud
mutation first: call ctx.api.v2Workspace.updateNameFromHost.mutate({ id:
cloudRow.id, branch: deduped, expectedCurrentName: oldBranchName? }) and only
update the local DB and perform git branch -m on success (or if git was already
renamed, revert it on cloud failure by running
worktreeGit.raw(["branch","-m",deduped,oldBranchName"])), or (B) keep the
current order but make the catch log actionable and unique (include cloudRow.id,
oldBranchName, deduped and the error) so divergence can be detected and retried;
apply the chosen fix around the block that calls worktreeGit.raw and
ctx.api.v2Workspace.updateNameFromHost.mutate.

---

Nitpick comments:
In `@packages/chat/src/server/desktop/title-generation/structured-generation.ts`:
- Around line 45-51: The current dynamic import block throws a generic Error if
Agent is missing which turns an optional dependency into a hard failure;
instead, in the context of generateWorkspaceNamesFromPrompt keep the operation
best-effort by returning null when Agent is not available. Locate the dynamic
import using agentModuleId and the Agent symbol (Agent?: StructuredAgentCtor) in
structured-generation.ts and replace the throw new Error("Mastra Agent
constructor is unavailable") with a return null (or otherwise ensure the
surrounding generateWorkspaceNamesFromPrompt returns null) so the caller treats
this as a skip-rename case rather than a 500.

In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`:
- Around line 17-24: The branchName zod rule is too strict (max set to
BRANCH_NAME_MAX/~25) causing valid AI outputs to be rejected and
generateObjectFromMessage to return null; relax the schema by increasing the max
(e.g. `.max(60)`) on branchName instead of the current BRANCH_NAME_MAX so longer
but valid kebab-case suggestions pass validation, then rely on
sanitizeBranchCandidate to truncate to BRANCH_NAME_MAX (the real hard cap)
before use; update the branchName.describe text if needed and keep references to
branchName, BRANCH_NAME_MAX, sanitizeBranchCandidate, and
generateObjectFromMessage when making the change.

In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`:
- Around line 1018-1049: After successfully renaming the branch in
generateWorkspaceNamesFromPrompt's code path (the block that calls
ctx.git(worktreePath).raw(["branch","-m", oldBranchName, deduped])), also rename
the on-disk worktree directory from the old sanitized name to the new one so the
.worktrees/<branchName> layout matches the branch ref; do this by resolving the
current worktreePath (used to create the worktree earlier), compute the new
worktree path for deduped (use the same sanitization/safeResolveWorktreePath
logic as when creating worktrees), check for collisions (fs.existsSync or
equivalent) and then fs.rename/move the directory, and handle errors (log and
fall back) so future safeResolveWorktreePath/git worktree add won't hit
misleading collisions. Ensure you reference worktreePath, oldBranchName,
deduped, ctx.git(...) and deduplicateBranchName in the change so reviewers can
find the spot.
🪄 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

Run ID: 14d27bdb-8db5-4772-a8f7-923d6dfced99

📥 Commits

Reviewing files that changed from the base of the PR and between 8b3ff23 and a2da692.

📒 Files selected for processing (7)
  • packages/chat/src/server/desktop/index.ts
  • packages/chat/src/server/desktop/title-generation/index.ts
  • packages/chat/src/server/desktop/title-generation/structured-generation.ts
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-name.ts
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
  • packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
  • packages/trpc/src/router/v2-workspace/v2-workspace.ts
💤 Files with no reviewable changes (1)
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-name.ts

Comment thread packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts Outdated
Comment on lines +1057 to +1067
if (aiNames.title && aiNames.title !== oldWorkspaceName) {
patch.name = aiNames.title;
patch.expectedCurrentName = oldWorkspaceName;
}
if (nextBranchName !== oldBranchName) {
patch.branch = nextBranchName;
}
if (patch.name === undefined && patch.branch === undefined) {
return;
}
await ctx.api.v2Workspace.updateNameFromHost.mutate(patch);
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.

⚠️ Potential issue | 🟡 Minor

No expectedCurrentBranch guard on the branch update.

patch.expectedCurrentName = oldWorkspaceName protects against a concurrent user rename of the display name, but there is no equivalent protection for branch. If the user manually renames the git branch (or the branch gets updated by some other flow) between the local workspaces.branch write above and this cloud mutation, the AI-suggested deduped will clobber their edit in the cloud row.

In practice, users rarely edit branch names in the seconds after creation, so this is low-probability. But the asymmetry with the name guard is worth either a comment explaining why it's intentional or a matching expectedCurrentBranch pattern on the server side.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`
around lines 1057 - 1067, The branch update lacks a concurrency guard similar to
expectedCurrentName, so add an expectedCurrentBranch check: when nextBranchName
!== oldBranchName set patch.branch = nextBranchName and also set
patch.expectedCurrentBranch = oldBranchName before calling
ctx.api.v2Workspace.updateNameFromHost.mutate(patch); if the server API doesn't
yet accept expectedCurrentBranch, either add that parameter on the server
handler or include a clear comment in workspace-creation.ts (near the
patch/branch logic) explaining why the asymmetry is intentional.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts">

<violation number="1" location="packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts:1037">
P1: Partial-commit risk: `git branch -m` and the local DB update (`ctx.db.update(workspaces).set({ branch: deduped })`) execute and commit before `updateNameFromHost.mutate(patch)` is called. If the remote mutate fails (network error, JWT expiry, workspace deleted, etc.), the local git branch and `workspaces.branch` carry the new name while `v2_workspaces.branch` in the cloud retains the old value — leaving a permanent desync.

Consider moving the local DB update to after the remote mutate succeeds, or adding a rollback (`git branch -m deduped oldBranchName` + reverting the local DB) in the outer catch block.</violation>
</file>

<file name="packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts">

<violation number="1" location="packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts:21">
P1: The `.max(BRANCH_NAME_MAX)` constraint on `branchName` in the zod schema causes the entire structured-output result to be rejected (returning `null`) when the model generates a branch name longer than 25 characters — `sanitizeBranchCandidate` with its `.slice(0, BRANCH_NAME_MAX)` never gets a chance to run. With a 25-char ceiling, LLM overshoots are very likely in practice, silently falling back to the friendly-fallback name.

Replace `.max(BRANCH_NAME_MAX)` with a `.transform()` that applies `sanitizeBranchCandidate` inside the schema so over-length responses are truncated rather than rejected.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

…ized AI names

Addresses two review findings on v2 AI rename:

- Partial-commit: previously `git branch -m` + host-local `workspaces.branch` were
  written before the cloud mutate, so a cloud failure left git + local in the new
  state and the cloud (and the web app) stuck on the old branch. Now we git-rename
  first, push name+branch to cloud, and only update host-local on cloud success;
  on cloud throw we git-rename back to the old name.
- `.max()` on the zod schema rejected any overlong model output, silently no-op'ing
  the whole rename. Replaced with `.transform()` pipes that trim the title and
  sanitize the branch so overshoots are coerced instead of dropped.
…tle slug

- Title cap bumped 40 → 150 and drops `.min(1)` — basically trust the small
  model with `.describe()` guidance.
- Branch falls back to a slug of the title when the model's branchName
  sanitizes to nothing (e.g. all emoji), so branch rename stops being a
  failure point.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts (1)

1046-1088: Optional: reuse the worktreeGit client for rollback.

The rollback path at line 1077-1085 opens a fresh git client via ctx.git(worktreePath) while worktreeGit (line 1047) is scoped to the if (branchChanged) block. Hoisting it makes the rollback cheaper and removes one potential failure surface (ctx.git() rejecting).

♻️ Proposed refactor
 						let deduped = oldBranchName;
 						let gitRenamed = false;
+						let worktreeGit: GitClient | null = null;
 						if (branchChanged) {
 							const freshBranches = await listBranchNames(
 								ctx,
 								localProject.repoPath,
 							);
 							deduped = deduplicateBranchName(
 								aiNames.branchName,
 								freshBranches.filter((b) => b !== oldBranchName),
 							);
 							try {
-								const worktreeGit = await ctx.git(worktreePath);
+								worktreeGit = await ctx.git(worktreePath);
 								await worktreeGit.raw(["branch", "-m", oldBranchName, deduped]);
 								gitRenamed = true;
 							} catch (err) {
 								console.warn(
 									"[workspaceCreation.create] git branch rename failed",
 									err,
 								);
 							}
 						}
@@
 						try {
 							await ctx.api.v2Workspace.updateNameFromHost.mutate(patch);
 						} catch (err) {
-							if (gitRenamed) {
-								await ctx
-									.git(worktreePath)
-									.then((g) => g.raw(["branch", "-m", deduped, oldBranchName]))
+							if (gitRenamed && worktreeGit) {
+								await worktreeGit
+									.raw(["branch", "-m", deduped, oldBranchName])
 									.catch((rollbackErr) => {
 										console.warn(
 											`[workspaceCreation.create] git branch rollback failed (workspace ${cloudRow.id}, ${deduped} → ${oldBranchName})`,
 											rollbackErr,
 										);
 									});
 							}
 							throw err;
 						}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`
around lines 1046 - 1088, Hoist the worktreeGit variable so the same git client
is used for both the initial rename and any rollback: declare let worktreeGit:
ReturnType<typeof ctx.git> | undefined before the branchChanged try block,
assign worktreeGit = await ctx.git(worktreePath) inside that try (then call
worktreeGit.raw([...]) and set gitRenamed = true), and in the catch of the
updateNameFromHost.mutate rollback use worktreeGit.raw(["branch", "-m", deduped,
oldBranchName]) if worktreeGit is defined instead of calling
ctx.git(worktreePath) again; preserve existing error handling and fallback
behavior if worktreeGit is undefined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts`:
- Around line 1046-1088: Hoist the worktreeGit variable so the same git client
is used for both the initial rename and any rollback: declare let worktreeGit:
ReturnType<typeof ctx.git> | undefined before the branchChanged try block,
assign worktreeGit = await ctx.git(worktreePath) inside that try (then call
worktreeGit.raw([...]) and set gitRenamed = true), and in the catch of the
updateNameFromHost.mutate rollback use worktreeGit.raw(["branch", "-m", deduped,
oldBranchName]) if worktreeGit is defined instead of calling
ctx.git(worktreePath) again; preserve existing error handling and fallback
behavior if worktreeGit is undefined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ec2a53b-7b7c-4489-aea6-b4258f7e2443

📥 Commits

Reviewing files that changed from the base of the PR and between a2da692 and 7b76d60.

📒 Files selected for processing (2)
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
  • packages/host-service/src/trpc/router/workspace-creation/workspace-creation.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts

Per-field gating in the caller already skips the branch rename when branchName
is empty; no need to salvage. If the model's output is bad, skip the rename.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`:
- Around line 20-25: trimTitle currently strips trailing punctuation before
truncating, so a punctuation character can remain if it falls exactly at
WORKSPACE_TITLE_MAX; update trimTitle (reference function trimTitle and compare
to sanitizeBranchCandidate behavior) to perform the truncation first then
re-apply the trailing-punctuation strip (e.g., trim/slice then
.replace(/[\s.,;:!?-]+$/g, "") and final .trim()) so titles are idempotently
free of trailing punctuation even after truncation.
🪄 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

Run ID: 1b6d3641-63b9-455f-a655-0edb0a3e0d3e

📥 Commits

Reviewing files that changed from the base of the PR and between 7b76d60 and 3c75c37.

📒 Files selected for processing (1)
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts

Comment on lines +20 to +25
function trimTitle(raw: string): string {
return raw
.trim()
.replace(/[\s.,;:!?-]+$/g, "")
.slice(0, WORKSPACE_TITLE_MAX);
}
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.

⚠️ Potential issue | 🟡 Minor

Consider re-trimming trailing punctuation after slice.

Unlike sanitizeBranchCandidate (which re-applies /-+$/g after truncation on line 17), trimTitle strips trailing punctuation before slice(0, 150). If the model emits a title longer than WORKSPACE_TITLE_MAX and character 150 happens to be whitespace, ., ,, -, etc., it will survive into v2_workspaces.name. Low likelihood at 150 chars, but trivial to make idempotent:

Proposed tweak
 function trimTitle(raw: string): string {
 	return raw
 		.trim()
 		.replace(/[\s.,;:!?-]+$/g, "")
-		.slice(0, WORKSPACE_TITLE_MAX);
+		.slice(0, WORKSPACE_TITLE_MAX)
+		.replace(/[\s.,;:!?-]+$/g, "");
 }
📝 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
function trimTitle(raw: string): string {
return raw
.trim()
.replace(/[\s.,;:!?-]+$/g, "")
.slice(0, WORKSPACE_TITLE_MAX);
}
function trimTitle(raw: string): string {
return raw
.trim()
.replace(/[\s.,;:!?-]+$/g, "")
.slice(0, WORKSPACE_TITLE_MAX)
.replace(/[\s.,;:!?-]+$/g, "");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`
around lines 20 - 25, trimTitle currently strips trailing punctuation before
truncating, so a punctuation character can remain if it falls exactly at
WORKSPACE_TITLE_MAX; update trimTitle (reference function trimTitle and compare
to sanitizeBranchCandidate behavior) to perform the truncation first then
re-apply the trailing-punctuation strip (e.g., trim/slice then
.replace(/[\s.,;:!?-]+$/g, "") and final .trim()) so titles are idempotently
free of trailing punctuation even after truncation.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts">

<violation number="1" location="packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts:24">
P2: `trimTitle` truncates with a raw slice, so titles can still be cut mid-word (and may end with punctuation after truncation), which violates the new title constraints.</violation>

<violation number="2" location="packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts:24">
P3: Re-trim trailing punctuation after `slice` — if the model returns a title longer than 150 chars and the cut lands on a space, comma, period, etc., those characters will leak into the workspace name. `sanitizeBranchCandidate` already handles this correctly by re-applying its cleanup regex after truncation; `trimTitle` should do the same.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

return raw
.trim()
.replace(/[\s.,;:!?-]+$/g, "")
.slice(0, WORKSPACE_TITLE_MAX);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 23, 2026

Choose a reason for hiding this comment

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

P2: trimTitle truncates with a raw slice, so titles can still be cut mid-word (and may end with punctuation after truncation), which violates the new title constraints.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts, line 24:

<comment>`trimTitle` truncates with a raw slice, so titles can still be cut mid-word (and may end with punctuation after truncation), which violates the new title constraints.</comment>

<file context>
@@ -2,23 +2,43 @@ import { generateObjectFromMessage } from "@superset/chat/server/desktop";
+	return raw
+		.trim()
+		.replace(/[\s.,;:!?-]+$/g, "")
+		.slice(0, WORKSPACE_TITLE_MAX);
+}
+
</file context>
Fix with Cubic

return raw
.trim()
.replace(/[\s.,;:!?-]+$/g, "")
.slice(0, WORKSPACE_TITLE_MAX);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 23, 2026

Choose a reason for hiding this comment

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

P3: Re-trim trailing punctuation after slice — if the model returns a title longer than 150 chars and the cut lands on a space, comma, period, etc., those characters will leak into the workspace name. sanitizeBranchCandidate already handles this correctly by re-applying its cleanup regex after truncation; trimTitle should do the same.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts, line 24:

<comment>Re-trim trailing punctuation after `slice` — if the model returns a title longer than 150 chars and the cut lands on a space, comma, period, etc., those characters will leak into the workspace name. `sanitizeBranchCandidate` already handles this correctly by re-applying its cleanup regex after truncation; `trimTitle` should do the same.</comment>

<file context>
@@ -2,23 +2,43 @@ import { generateObjectFromMessage } from "@superset/chat/server/desktop";
+	return raw
+		.trim()
+		.replace(/[\s.,;:!?-]+$/g, "")
+		.slice(0, WORKSPACE_TITLE_MAX);
+}
+
</file context>
Suggested change
.slice(0, WORKSPACE_TITLE_MAX);
.slice(0, WORKSPACE_TITLE_MAX)
.replace(/[\s.,;:!?-]+$/g, "");
Fix with Cubic

…erate directly

The wrapper in packages/chat was pure indirection — chat already has @mastra/core
as a direct dep. Removed it, added @mastra/core to host-service, and call
agent.generate({ structuredOutput }) directly from ai-workspace-names. Also tightened
getSmallModel's return type to MastraModelConfig | null so callers don't need casts.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts (1)

20-25: ⚠️ Potential issue | 🟡 Minor

Post-slice trailing punctuation can still survive.

Same asymmetry as flagged previously: sanitizeBranchCandidate re-applies /-+$/g after slice (L17), but trimTitle strips punctuation before truncation only, so if char 150 lands on whitespace/.,;:!?-, it will persist into v2_workspaces.name. Low-probability at 150 chars, but trivial to make idempotent.

Proposed tweak
 function trimTitle(raw: string): string {
 	return raw
 		.trim()
 		.replace(/[\s.,;:!?-]+$/g, "")
-		.slice(0, WORKSPACE_TITLE_MAX);
+		.slice(0, WORKSPACE_TITLE_MAX)
+		.replace(/[\s.,;:!?-]+$/g, "");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`
around lines 20 - 25, trimTitle currently strips trailing punctuation before
truncating which can leave post-slice trailing punctuation in the result; update
trimTitle (the function named trimTitle that uses WORKSPACE_TITLE_MAX) to
perform the truncation first (slice to WORKSPACE_TITLE_MAX) and then re-apply
the trailing-punctuation removal (same regex style used in
sanitizeBranchCandidate, e.g. /[\s.,;:!?-]+$/g) so the returned title is
idempotently free of trailing punctuation even when truncation cuts mid-run of
punctuation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts`:
- Around line 20-25: trimTitle currently strips trailing punctuation before
truncating which can leave post-slice trailing punctuation in the result; update
trimTitle (the function named trimTitle that uses WORKSPACE_TITLE_MAX) to
perform the truncation first (slice to WORKSPACE_TITLE_MAX) and then re-apply
the trailing-punctuation removal (same regex style used in
sanitizeBranchCandidate, e.g. /[\s.,;:!?-]+$/g) so the returned title is
idempotently free of trailing punctuation even when truncation cuts mid-run of
punctuation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ddd592d-3999-4ee8-bc37-9bd5508c18ff

📥 Commits

Reviewing files that changed from the base of the PR and between 3c75c37 and 9876d09.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • packages/chat/src/server/shared/small-model/get-small-model.ts
  • packages/host-service/package.json
  • packages/host-service/src/trpc/router/workspace-creation/utils/ai-workspace-names.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/host-service/package.json

Pulls the ~80-line post-create block out of workspaceCreation.create into
applyAiWorkspaceRename in ai-workspace-names.ts — same file as the generator
so naming + applying live together. listBranchNames moves to its own util so
both callers can share it.

Call site in the create handler is now a three-line fire-and-forget.
@saddlepaddle saddlepaddle merged commit aceb0fd into main Apr 24, 2026
14 checks passed
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 24, 2026
Sherif の multiple-dependency-versions エラーを解消。upstream superset-sh#3692
(aceb0fd) が host-service の @mastra/core を 1.26.0-alpha.3 に
アップデートしたが、apps/desktop と packages/chat は 1.25.0 のまま
だった。upstream/main では 3 箇所全て 1.26.0-alpha.3 で統一されているため、
fork でも揃える。
@Kitenite Kitenite deleted the boiling-silver branch May 6, 2026 04:52
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