Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions WORKER_NAMING_EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Worker Naming Examples

This document demonstrates the new task-based worker naming feature.

## Before (Random Names)

```bash
$ multiclaude worker create "Fix session ID bug in authentication"
Creating worker 'calm-owl' in repo 'myproject'

$ multiclaude worker create "Add user profile editing"
Creating worker 'jolly-hawk' in repo 'myproject'
```

Workers had random adjective-animal names that provided no context about their purpose.

## After (Task-Based Names)

```bash
$ multiclaude worker create "Fix session ID bug in authentication"
Creating worker 'fix-session-id-bug' in repo 'myproject'

$ multiclaude worker create "Add user profile editing"
Creating worker 'add-user-profile-editing' in repo 'myproject'
```

Workers now have descriptive names derived from their task descriptions.

## How It Works

### 1. Keyword Extraction

The system extracts meaningful keywords from the task description:

```
Task: "Fix the session ID bug in authentication"
Keywords: ["fix", "session", "id", "bug"] (stop words removed: "the", "in")
Name: "fix-session-id-bug"
```

### 2. Sanitization

Names are converted to valid format:

- Lowercase letters only
- Hyphens separate words
- Special characters removed
- Maximum 50 characters

```
Task: "Update API (v2) endpoint configuration!!!"
Keywords: ["update", "api", "v2", "endpoint"]
Name: "update-api-v2-endpoint"
```

### 3. Uniqueness

Duplicate names get numeric suffixes:

```bash
$ multiclaude worker create "Fix bug in login"
Creating worker 'fix-bug-login' in repo 'myproject'

$ multiclaude worker create "Fix bug in login" # Same task
Creating worker 'fix-bug-login-2' in repo 'myproject'

$ multiclaude worker create "Fix bug in login" # Again
Creating worker 'fix-bug-login-3' in repo 'myproject'
```

### 4. Fallback to Random Names

If the task description is invalid or too short, the system falls back to random names:

```bash
$ multiclaude worker create "!!!"
Creating worker 'happy-platypus' in repo 'myproject' # Fallback

$ multiclaude worker create "the a an is" # Only stop words
Creating worker 'clever-dolphin' in repo 'myproject' # Fallback
```

## Manual Override

The `--name` flag still works for manual naming:

```bash
$ multiclaude worker create "Fix bug" --name my-custom-name
Creating worker 'my-custom-name' in repo 'myproject'
```

## Real-World Examples

| Task Description | Generated Name |
|-----------------|----------------|
| "Fix memory leak in database connection pool" | `fix-memory-leak-database` |
| "Implement OAuth2 authentication flow" | `implement-oauth2-authentication-flow` |
| "Refactor user service to use new API" | `refactor-user-service-new` |
| "Add unit tests for payment module" | `add-unit-tests-payment` |
| "Update README with installation instructions" | `update-readme-installation-instructions` |
| "Debug timeout in webhook handler" | `debug-timeout-webhook-handler` |

## Benefits

1. **Clarity**: Immediately understand what each worker is doing
2. **Tracking**: Easier to monitor worker progress in logs and tmux
3. **Git branches**: Branch names like `work/fix-session-id-bug` are self-documenting
4. **PR identification**: PRs are easier to identify from their branch names
5. **Debugging**: When something goes wrong, you know which worker to investigate

## Technical Details

- Implementation: `internal/names/names.go`
- Tests: `internal/names/names_test.go`
- Specification: `WORKER_NAMING_SPEC.md`
- Integration: `internal/cli/cli.go:createWorker()`
106 changes: 106 additions & 0 deletions WORKER_NAMING_SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Worker Naming Specification

## Overview

Workers should have descriptive, task-based names instead of random adjective-animal combinations. This makes it easier to identify what each worker is doing at a glance.

## Requirements

### 1. Task Summary Extraction

Extract a 3-4 word summary from the task description using heuristic processing:

- Remove common stop words (a, an, the, is, are, to, for, in, on, at, etc.)
- Identify and extract meaningful keywords (nouns, verbs, technical terms)
- Prioritize words at the beginning of the task description
- Limit to 3-4 words to keep names concise

### 2. Sanitization

Convert the extracted summary to a valid worker name:

- Convert to lowercase
- Replace spaces with hyphens
- Remove or replace special characters (keep only alphanumeric and hyphens)
- Collapse multiple consecutive hyphens into one
- Trim leading/trailing hyphens
- Maximum length: 50 characters (truncate if needed)

### 3. Uniqueness Handling

Ensure worker names are unique within a repository:

- Check if the generated name already exists
- If it exists, append numeric suffix: `-2`, `-3`, etc.
- Keep incrementing until a unique name is found

### 4. Fallback Strategy

If task extraction fails or produces invalid names:

- Fall back to the existing random name generator (`names.Generate()`)
- This ensures workers can always be created, even with unusual task descriptions

### 5. Manual Override

Preserve the existing `--name` flag to allow users to manually specify worker names.

## Examples

| Task Description | Generated Name |
|-----------------|----------------|
| "Fix the session ID bug in authentication" | `fix-session-id-bug` |
| "Add user profile editing feature" | `add-user-profile` |
| "Refactor the database connection logic" | `refactor-database-connection` |
| "Update README documentation" | `update-readme-documentation` |
| "Implement OAuth2 login flow" | `implement-oauth2-login` |
| "Fix bug" (too short) | `fix-bug` |
| "!!!" (invalid) | `happy-platypus` (fallback) |

## Implementation Details

### Stop Words List

Common words to filter out:
```
a, an, the, is, are, am, was, were, be, been, being, have, has, had,
do, does, did, will, would, should, could, may, might, must, can,
to, for, of, in, on, at, by, with, from, as, into, through,
this, that, these, those, it, its, they, their, there, here,
and, or, but, if, because, when, where, how, what, which, who, why
```

### Name Validation

A valid worker name must:
- Be between 3 and 50 characters long
- Contain at least one alphabetic character
- Not start or end with a hyphen
- Contain only lowercase letters, numbers, and hyphens

### Edge Cases

- Empty task description → fallback to random name
- Task with only stop words → fallback to random name
- Task producing name less than 3 characters → fallback to random name
- Very long task → extract key terms and truncate
- Special characters in task → sanitize and remove
- Duplicate name → append numeric suffix

## Testing Requirements

Comprehensive tests must cover:

1. **Basic extraction**: Verify correct keyword extraction from various task descriptions
2. **Sanitization**: Test lowercase conversion, special character handling, hyphen collapsing
3. **Uniqueness**: Test numeric suffix appending for duplicate names
4. **Fallback**: Verify fallback to random names for invalid inputs
5. **Edge cases**: Empty strings, very long strings, special characters only
6. **Integration**: Test within the full worker creation flow

## Migration

Existing code should continue to work:
- The `names.Generate()` function remains available for backward compatibility
- New function `names.FromTask(task string)` implements the task-based naming
- CLI code updated to use `FromTask()` by default, with `Generate()` as fallback
40 changes: 36 additions & 4 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -1932,10 +1932,42 @@ func (c *CLI) createWorker(args []string) error {
return errors.NotInRepo()
}

// Generate worker name (Docker-style)
workerName := names.Generate()
// Get existing agents to ensure unique naming
client := socket.NewClient(c.paths.DaemonSock)
resp, err := client.Send(socket.Request{
Command: "list_agents",
Args: map[string]interface{}{
"repo": repoName,
},
})
if err != nil {
return errors.DaemonCommunicationFailed("getting existing agents", err)
}
if !resp.Success {
return errors.Wrap(errors.CategoryRuntime, "failed to get existing agents", fmt.Errorf("%s", resp.Error))
}

// Extract existing worker names for uniqueness check
var existingNames []string
if agents, ok := resp.Data.([]interface{}); ok {
for _, agent := range agents {
if agentMap, ok := agent.(map[string]interface{}); ok {
if agentName, ok := agentMap["name"].(string); ok {
existingNames = append(existingNames, agentName)
}
}
}
}

// Generate worker name from task description
var workerName string
if name, ok := flags["name"]; ok {
// Manual override via --name flag
workerName = name
} else {
// Generate task-based name and ensure uniqueness
workerName = names.FromTask(task)
workerName = names.EnsureUnique(workerName, existingNames)
}

// Check for --push-to flag (for iterating on existing PRs)
Expand Down Expand Up @@ -2022,8 +2054,8 @@ func (c *CLI) createWorker(args []string) error {
}

// Get repository info to determine tmux session
client := socket.NewClient(c.paths.DaemonSock)
resp, err := client.Send(socket.Request{
client = socket.NewClient(c.paths.DaemonSock)
resp, err = client.Send(socket.Request{
Command: "list_agents",
Args: map[string]interface{}{
"repo": repoName,
Expand Down
Loading