Skip to main content

How it works

This page walks through the complete execution flow of a Longshot build, from user request to delivered project.

Execution overview

┌──────────────────────────────────────────────────────────────────┐
│ Phase 1: Initial Planning                                        │
│  User request → Root planner analyzes repo → Creates tasks      │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ Phase 2: Parallel Execution                                      │
│  Tasks dispatch to workers → Sandboxes execute → Branches push  │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ Phase 3: Merge Queue Processing                                 │
│  Branches merge sequentially → Conflicts retry or fix           │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ Phase 4: Continuous Reconciliation                              │
│  Build/test checks → Fix task generation → Re-dispatch         │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ Phase 5: Finalization                                           │
│  Drain queue → Retry unmerged → Final sweep → Report          │
└──────────────────────────────────────────────────────────────────┘

Example scenario

Let’s trace a realistic build request through the entire system: User request:
longshot "Build a REST API with user authentication and todo CRUD according to SPEC.md"
Repository state:
  • SPEC.md defines API endpoints and data models
  • FEATURES.json lists planned features
  • Empty src/ directory
  • package.json with Express + TypeScript

Phase 1: Initial planning

Step 1.1: Orchestrator startup

// main.ts
const orchestrator = await createOrchestrator({
  projectRoot: process.cwd(),
  maxIterations: 100,
  callbacks: {
    onTaskCreated: (task) => console.log(`Created ${task.id}`),
    onTaskCompleted: (task, handoff) => console.log(`Completed ${task.id}`)  
  }
});

await orchestrator.run("Build a REST API with user authentication and todo CRUD");
Component initialization:
  1. Load configuration from .env
  2. Read system prompts from prompts/ directory
  3. Wire components: planner → worker pool → merge queue → reconciler
  4. Start background services (monitor, reconciler, merge queue)

Step 1.2: Root planner initialization

// planner.ts - runLoop()
const piSession = await createPlannerPiSession({
  systemPrompt: rootPlannerPrompt,
  targetRepoPath: config.targetRepoPath,
  llmConfig: config.llm
});
Pi session creation:
  • Creates temporary directory for planner workspace
  • Initializes LLM session with root-planner system prompt
  • Sets up tool access (bash, read, write, edit, glob, grep)

Step 1.3: Repository analysis

const repoState = await readRepoState(targetRepoPath);

// Returns:
{
  fileTree: [
    "package.json",
    "tsconfig.json",
    "SPEC.md",
    "FEATURES.json",
    "src/"
  ],
  recentCommits: [
    "abc123 Initial commit"
  ],
  specMd: "# API Specification\n\n## Endpoints\n...",
  featuresJson: "[{\"id\": \"auth\", \"status\": \"planned\"}, ...]",
  agentsMd: "# Coding Conventions\n...",
  decisionsMd: "# Architecture Decisions\n..."
}

Step 1.4: Initial planning prompt

The planner constructs a comprehensive initial prompt:
const prompt = `
## Request
${request}

## SPEC.md (Product Specification)
${repoState.specMd}

## FEATURES.json
${repoState.featuresJson}

## AGENTS.md (Coding Conventions)
${repoState.agentsMd}

## Repository File Tree
${repoState.fileTree.join('\n')}

## Recent Commits
${repoState.recentCommits.join('\n')}

This is the initial planning call. SPEC.md and FEATURES.json are binding.
Produce your first batch of tasks and scratchpad.
`;

await piSession.session.prompt(prompt);

Step 1.5: LLM response and task parsing

LLM response (example):
{
  "scratchpad": "The request requires 5 main tasks: (1) Express server setup, (2) Auth middleware with JWT, (3) User registration endpoint, (4) Todo CRUD (complex, 5 files), (5) Integration tests. Task 4 should go to subplanner due to scope.",
  "tasks": [
    {
      "id": "task-001",
      "description": "Set up Express server with TypeScript, middleware, and error handling",
      "scope": ["src/server.ts", "src/middleware/error-handler.ts"],
      "acceptance": "Server starts on port 3000, handles 404s, returns JSON errors",
      "priority": 5
    },
    {
      "id": "task-002",
      "description": "Implement JWT authentication middleware with token verification",
      "scope": ["src/middleware/auth.ts", "src/utils/jwt.ts"],
      "acceptance": "Middleware validates JWT tokens, returns 401 for invalid/missing tokens",
      "priority": 5
    },
    {
      "id": "task-003",
      "description": "Create user registration endpoint with password hashing",
      "scope": ["src/routes/auth.ts", "src/models/user.ts"],
      "acceptance": "POST /auth/register creates user with bcrypt-hashed password, returns JWT",
      "priority": 5
    },
    {
      "id": "task-004",
      "description": "Create todo CRUD endpoints (GET, POST, PUT, DELETE)",
      "scope": [
        "src/routes/todos.ts",
        "src/models/todo.ts",
        "src/controllers/todo.controller.ts",
        "src/middleware/validate-todo.ts",
        "src/types/todo.types.ts"
      ],
      "acceptance": "All CRUD operations work, todos are user-scoped, validation errors return 400",
      "priority": 5
    },
    {
      "id": "task-005",
      "description": "Add API integration tests with supertest",
      "scope": ["src/__tests__/api.test.ts"],
      "acceptance": "Tests cover auth + todo CRUD, all tests pass",
      "priority": 5
    }
  ]
}
Task parsing:
const { scratchpad, tasks: rawTasks } = parsePlannerResponse(responseText);

const tasks: Task[] = rawTasks.map((raw) => ({
  id: raw.id || `task-${++taskCounter}`,
  description: raw.description,
  scope: raw.scope || [],
  acceptance: raw.acceptance || '',
  branch: raw.branch || `worker/${raw.id}-${slugify(raw.description)}`,
  status: 'pending',
  createdAt: Date.now(),
  priority: raw.priority || 5
}));

// Result: 5 tasks ready for dispatch

Phase 2: Parallel execution

Step 2.1: Task dispatch and routing

for (const task of tasks) {
  taskQueue.enqueue(task);
  dispatchedTaskIds.add(task.id);
  activeTasks.add(task.id);
  
  // Fire-and-forget dispatch
  dispatchSingleTask(task);
}
Routing logic:
async dispatchSingleTask(task: Task) {
  await dispatchLimiter.acquire(); // Concurrency control
  
  // Route based on scope size
  if (shouldDecompose(task, config, 0)) {
    // task-004 has 5 files → route to subplanner
    handoff = await subplanner.decomposeAndExecute(task, 0);
  } else {
    // task-001, task-002, task-003, task-005 → direct to workers
    handoff = await workerPool.assignTask(task);
  }
  
  pendingHandoffs.push({ task, handoff });
  dispatchLimiter.release();
}
Dispatch result:
  • Tasks 001, 002, 003, 005 → Worker pool (parallel)
  • Task 004 → Subplanner (will decompose further)

Step 2.2: Subplanner decomposition (task-004)

// subplanner.ts - decomposeAndExecute()
const piSession = await createPlannerPiSession({
  systemPrompt: subplannerPrompt,
  targetRepoPath,
  llmConfig
});

const prompt = `
## Parent Task
- **ID**: task-004
- **Description**: Create todo CRUD endpoints (GET, POST, PUT, DELETE)
- **Scope**: src/routes/todos.ts, src/models/todo.ts, ...
- **Decomposition Depth**: 0

## Repository File Tree
${fileTree.join('\n')}

Decompose this task into atomic subtasks. Return JSON.
`;

await piSession.session.prompt(prompt);
LLM decomposes into subtasks:
{
  "scratchpad": "Breaking todo CRUD into 5 atomic tasks: model definition, GET endpoint, POST endpoint, PUT endpoint, DELETE endpoint.",
  "tasks": [
    {
      "id": "task-004-sub-1",
      "description": "Create todo model and TypeScript types",
      "scope": ["src/models/todo.ts", "src/types/todo.types.ts"],
      "acceptance": "Todo model exports interface and validation schema",
      "priority": 5
    },
    {
      "id": "task-004-sub-2",
      "description": "Create GET /todos and GET /todos/:id endpoints",
      "scope": ["src/routes/todos.ts", "src/controllers/todo.controller.ts"],
      "acceptance": "GET endpoints return user's todos, 404 for missing ID",
      "priority": 5
    },
    // ... task-004-sub-3, sub-4, sub-5
  ]
}
Subtask dispatch:
for (const subtask of subtasks) {
  taskQueue.enqueue(subtask);
  
  // All subtasks are atomic (scope < 4) → dispatch to workers
  handoff = await workerPool.assignTask(subtask);
  
  subtaskHandoffs.push(handoff);
}

// Aggregate all subtask results into parent handoff
return aggregateHandoffs(task004, subtasks, subtaskHandoffs);
Total parallel workers: 9 (tasks 001, 002, 003, 005 + 5 subtasks of 004)

Step 2.3: Worker sandbox execution

Each worker follows this lifecycle: Python sandbox spawner (infra/spawn_sandbox.py):
import modal
import json
import sys

payload = json.loads(sys.argv[1])
task = payload['task']
repo_url = payload['repoUrl']
llm_config = payload['llmConfig']

# Create Modal sandbox
sandbox = modal.Sandbox.create(
    "ubuntu-22.04",
    cpu=4,
    memory=8192,
    timeout=1800
)

print("[spawn] Sandbox created", flush=True)

# Clone repository
sandbox.exec(f"git clone {repo_url} /workspace")
print("[spawn] Repo cloned", flush=True)

# Checkout task branch
sandbox.exec(
    f"cd /workspace && git checkout -b {task['branch']}"
)
print(f"[spawn] Checked out branch {task['branch']}", flush=True)

# Write task to sandbox
task_json = json.dumps(task)
sandbox.exec(f"echo '{task_json}' > /workspace/task.json")

# Start Pi agent (worker-runner.ts)
result = sandbox.exec(
    "node",
    args=["/app/worker-runner.js"],
    env={
        "TASK_FILE": "/workspace/task.json",
        "LLM_ENDPOINT": llm_config['endpoint'],
        "LLM_MODEL": llm_config['model'],
        "LLM_API_KEY": llm_config['apiKey']
    },
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

print(f"[spawn] Worker completed with exit code {result.returncode}", flush=True)

# Push branch to remote
sandbox.exec(
    f"cd /workspace && git push origin {task['branch']}"
)
print(f"[spawn] Pushed branch {task['branch']}", flush=True)

# Read handoff result
handoff_json = sandbox.exec("cat /workspace/handoff.json").stdout
handoff = json.loads(handoff_json)

# Print handoff as final line (parsed by TypeScript)
print(json.dumps(handoff), flush=True)
Pi agent execution (packages/sandbox/src/worker-runner.ts):
import { Pi } from '@mariozechner/pi-coding-agent';
import fs from 'fs';

const task = JSON.parse(fs.readFileSync('/workspace/task.json', 'utf-8'));

const pi = new Pi({
  systemPrompt: workerSystemPrompt,
  workingDirectory: '/workspace',
  llm: {
    endpoint: process.env.LLM_ENDPOINT,
    model: process.env.LLM_MODEL,
    apiKey: process.env.LLM_API_KEY
  }
});

// Construct user prompt
const prompt = `
## Task
${task.description}

## Scope
Focus on these files: ${task.scope.join(', ')}

## Acceptance Criteria
${task.acceptance}

## Instructions
- Use bash, read, write, edit tools to complete the task
- Commit changes with descriptive message
- Ensure code compiles (run tsc --noEmit)
- Write handoff.json with summary, concerns, suggestions
`;

const result = await pi.run(prompt);

// Agent executes tools, writes code, commits
// Example tool sequence:
//   1. read('src/routes/todos.ts')  // Check existing code
//   2. write('src/routes/todos.ts', content)  // Create file
//   3. bash('tsc --noEmit')  // Verify compilation
//   4. bash('git add src/routes/todos.ts')
//   5. bash('git commit -m "Add todo routes"')

// Build handoff report
const diff = execSync('git diff HEAD~1').toString();
const stats = execSync('git diff --numstat HEAD~1').toString();

const handoff = {
  taskId: task.id,
  status: result.success ? 'complete' : 'failed',
  summary: result.summary,
  diff,
  filesChanged: task.scope,
  concerns: result.concerns,
  suggestions: result.suggestions,
  metrics: {
    tokensUsed: result.tokens,
    toolCallCount: result.toolCalls,
    durationMs: result.duration,
    linesAdded: parseStats(stats).added,
    linesRemoved: parseStats(stats).removed,
    filesCreated: task.scope.length,
    filesModified: task.scope.length
  }
};

fs.writeFileSync('/workspace/handoff.json', JSON.stringify(handoff));
Parallel execution timeline:
t=0s:  All 9 workers spawn sandboxes simultaneously
t=10s: Repos cloned, branches created
t=20s: Pi agents start executing
t=90s: task-001 completes (simple server setup)
t=120s: task-002 completes (auth middleware)
t=150s: task-004-sub-1 completes (todo model)
t=180s: task-003 completes (registration endpoint)
t=200s: task-004-sub-2 completes (GET endpoints)
t=220s: task-004-sub-3 completes (POST endpoint)
t=240s: task-004-sub-4 completes (PUT endpoint)
t=260s: task-004-sub-5 completes (DELETE endpoint)
t=300s: task-005 completes (integration tests)

Phase 3: Merge queue processing

Step 3.1: Handoff collection

As workers complete, the planner collects handoffs:
while (pendingHandoffs.length > 0) {
  const { task, handoff } = pendingHandoffs.shift();
  
  allHandoffs.push(handoff);
  handoffsSinceLastPlan.push(handoff);
  
  if (handoff.status === 'complete') {
    // Enqueue branch for merging
    mergeQueue.enqueue(task.branch, task.priority);
  } else if (handoff.status === 'failed' && task.retryCount < 1) {
    // Auto-retry failed tasks once
    taskQueue.retryTask(task.id);
    dispatchSingleTask(task);
  }
}

Step 3.2: Background merge processing

The merge queue processes branches in priority order:
// Merge queue runs every 5 seconds
while (queue.length > 0) {
  const branch = dequeue(); // e.g., "worker/task-001-server-setup"
  const result = await mergeBranch(branch);
  
  if (result.success) {
    merged.add(branch);
    stats.totalMerged++;
  } else if (result.conflicted) {
    handleConflict(branch, result.conflicts);
  } else {
    stats.totalFailed++;
  }
}
Merge timeline:
t=90s:  worker/task-001 → main (merged successfully)
t=120s: worker/task-002 → main (merged successfully)
t=150s: worker/task-004-sub-1 → main (merged successfully)
t=180s: worker/task-003 → main (CONFLICT: auth.ts overlaps with task-002)
        → Rebase onto latest main
        → Retry merge (attempt 1/2)
t=185s: worker/task-003 → main (merged after rebase)
t=200s: worker/task-004-sub-2 → main (merged successfully)
t=220s: worker/task-004-sub-3 → main (CONFLICT: todos.ts overlaps with sub-2)
        → Rebase onto latest main
        → Retry merge (attempt 1/2)
t=225s: worker/task-004-sub-3 → main (CONFLICT PERSISTS)
        → Retry exhausted (2/2)
        → Create conflict-fix-001 task

Step 3.3: Conflict resolution task

When retries are exhausted, the merge queue creates a conflict-fix task:
mergeQueue.onConflict((info) => {
  const fixTask = {
    id: `conflict-fix-${++conflictCounter}`,
    description: `
Resolve merge conflict from branch "${info.branch}".

Conflicting files: ${info.conflictingFiles.join(', ')}

The sandbox will check out the original branch and rebase it onto main.
Conflict markers will be present in the working tree.

Open each file, find <<<<<<< / ======= / >>>>>>> blocks, and resolve by
keeping the correct version based on surrounding code context.

Remove all conflict markers, run 'git add' on each file, then
'git rebase --continue'. Ensure the file compiles after resolution.
    `,
    scope: info.conflictingFiles.slice(0, 5),
    acceptance: "No <<<<<<< markers remain. tsc --noEmit returns 0. Rebase completes.",
    branch: info.branch,  // Reuse original branch
    status: 'pending',
    priority: 1,  // High priority
    conflictSourceBranch: info.branch
  };
  
  planner.injectTask(fixTask);
});
The conflict-fix task dispatches to a worker, which resolves conflicts manually using the Pi agent.

Phase 4: Continuous reconciliation

Step 4.1: Background health checks

While tasks execute and merge, the reconciler runs periodic sweeps:
// Reconciler sweep every 5 minutes (1 minute when errors detected)
setInterval(async () => {
  const result = await reconciler.sweep();
  
  if (!result.buildOk || !result.testsOk || result.hasConflictMarkers) {
    // Inject fix tasks into planner
    for (const fixTask of result.fixTasks) {
      planner.injectTask(fixTask);
    }
  }
}, intervalMs);
Sweep execution:
async sweep(): Promise<SweepResult> {
  // Check build
  const tscResult = await exec('npx', ['tsc', '--noEmit'], targetRepo);
  const buildOk = tscResult.code === 0;
  
  // Check tests
  const testResult = await exec('npm', ['test'], targetRepo);
  const testsOk = testResult.code === 0;
  
  // Check for conflict markers
  const conflictResult = await exec('git', ['grep', '-rl', '<<<<<<<'], targetRepo);
  const hasConflictMarkers = conflictResult.stdout.length > 0;
  
  if (buildOk && testsOk && !hasConflictMarkers) {
    return { buildOk: true, testsOk: true, fixTasks: [] };
  }
  
  // Build failed — analyze and create fix tasks
  const prompt = `
## Build Output
${tscResult.stderr}

## Test Output  
${testResult.stderr}

Analyze failures and generate fix tasks with specific scope.
  `;
  
  const response = await llm.complete([{ role: 'user', content: prompt }]);
  const fixTasks = parseTasks(response.content);
  
  return { buildOk, testsOk, hasConflictMarkers, fixTasks };
}
Example reconciler fix:
t=280s: Reconciler sweep detects test failure:
        "TypeError: Cannot read property 'id' of undefined at todo.controller.ts:42"
        
        LLM analyzes and generates:
        fix-001: "Add null check in getTodoById controller before accessing todo.id"
        scope: ["src/controllers/todo.controller.ts"]
        priority: 1
        
        Task dispatches to worker immediately (high priority).

Step 4.2: Follow-up planning

When 3 or more handoffs accumulate, the planner runs a follow-up planning iteration:
if (handoffsSinceLastPlan.length >= 3 || activeTasks.size === 0) {
  const repoState = await readRepoState(targetRepoPath);
  const prompt = buildFollowUpPrompt(repoState, handoffsSinceLastPlan);
  
  await piSession.session.prompt(prompt);
  const tasks = parseTasks(response);
  
  handoffsSinceLastPlan = [];
  
  if (tasks.length > 0) {
    dispatchTasks(tasks);
  } else if (activeTasks.size === 0) {
    planningDone = true;
  }
}
Follow-up prompt (delta-optimized):
const prompt = `
## Updated Repository State
File tree (${currentTree.size} files):
  New: src/controllers/todo.controller.ts, src/types/todo.types.ts
  Removed: (none)

Recent commits:
  def456 Add todo model and types
  ghi789 Add GET /todos endpoints

## New Worker Handoffs (3 since last plan)
### Task task-004-sub-1 — complete
Summary: Created todo model with Mongoose schema and TypeScript types.
Files changed: src/models/todo.ts, src/types/todo.types.ts
Concerns: None
Suggestions: Add validation for todo status enum

### Task task-004-sub-2 — complete
Summary: Implemented GET /todos and GET /todos/:id endpoints.
Files changed: src/routes/todos.ts, src/controllers/todo.controller.ts
Concerns: Missing error handling for invalid todo ID
Suggestions: Add 404 response for non-existent todos

### Task task-004-sub-3 — complete
Summary: Implemented POST /todos endpoint with validation.
Files changed: src/routes/todos.ts, src/controllers/todo.controller.ts
Concerns: None
Suggestions: None

## Currently Active Tasks (2)
- task-004-sub-4: Implement PUT /todos/:id endpoint
- task-004-sub-5: Implement DELETE /todos/:id endpoint

## Merge Queue Health
Merged: 7 | Conflicts: 1 | Failed: 0 | Queue depth: 2
Success rate: 88%

Continue planning. Review handoffs and emit next batch of tasks.
`;
The planner may decide:
  • “All core features complete, no new tasks needed”
  • OR: “Add error handling task based on worker concerns”

Phase 5: Finalization

Step 5.1: Wait for active tasks

while (activeTasks.size > 0) {
  collectCompletedHandoffs();
  await sleep(500);
}

logger.info('All tasks completed', {
  totalTasks: dispatchedTaskIds.size,
  completedTasks: completedCount,
  failedTasks: failedCount
});

Step 5.2: Drain merge queue

if (finalizationEnabled) {
  const mergeQueueDepth = mergeQueue.getQueueLength();
  
  if (mergeQueueDepth > 0) {
    logger.info('Draining merge queue', { depth: mergeQueueDepth });
    const results = await mergeQueue.processQueue();
    
    logger.info('Merge queue drained', {
      merged: results.filter(r => r.success).length,
      failed: results.filter(r => !r.success).length
    });
  }
}

Step 5.3: Re-attempt unmerged branches

const allBranches = planner.getAllDispatchedBranches();
const unmerged = allBranches.filter(b => !mergeQueue.isBranchMerged(b));

if (unmerged.length > 0) {
  logger.info('Re-enqueuing unmerged branches', { count: unmerged.length });
  
  for (const branch of unmerged) {
    mergeQueue.resetRetryCount(branch);
    mergeQueue.enqueue(branch, 1);  // High priority
  }
  
  await mergeQueue.processQueue();
}

Step 5.4: Final reconciliation sweep

logger.info('Running final build + test sweep');

const sweepResult = await reconciler.sweep();

if (sweepResult.buildOk && sweepResult.testsOk && !sweepResult.hasConflictMarkers) {
  logger.info('✅ Finalization PASSED — all green!');
  return { success: true };
}

if (sweepResult.fixTasks.length > 0) {
  logger.info('Issues detected, dispatching fix tasks', {
    count: sweepResult.fixTasks.length
  });
  
  for (const task of sweepResult.fixTasks) {
    planner.injectTask(task);
  }
  
  // Wait for fix tasks to complete
  while (planner.getActiveTaskCount() > 0) {
    await sleep(1000);
  }
  
  // Re-run sweep
  const finalSweep = await reconciler.sweep();
  return finalSweep;
}

Step 5.5: Final report

const snapshot = monitor.getSnapshot();

logger.info('🎉 Longshot run complete', {
  totalTasks: snapshot.completedTasks + snapshot.failedTasks,
  completedTasks: snapshot.completedTasks,
  failedTasks: snapshot.failedTasks,
  mergeSuccessRate: snapshot.mergeSuccessRate,
  totalTokensUsed: snapshot.totalTokensUsed,
  totalCostUsd: snapshot.totalCostUsd,
  finalizationBuildPassed: snapshot.finalizationBuildPassed,
  finalizationTestsPassed: snapshot.finalizationTestsPassed,
  finalizationAllMerged: snapshot.finalizationAllMerged
});

if (snapshot.finalizationAllMerged) {
  console.log('\n✅ All branches merged successfully');
} else {
  console.log(`\n⚠️  ${snapshot.finalizationUnmergedCount} branches remain unmerged`);
  console.log('Check remote branches for manual recovery');
}

Key execution patterns

Fire-and-forget dispatch

Workers dispatch asynchronously without blocking:
for (const task of tasks) {
  dispatchSingleTask(task); // Does not await
}

// All tasks start in parallel immediately

Concurrency limiting

class ConcurrencyLimiter {
  private active = 0;
  private queue: Array<() => void> = [];
  
  async acquire() {
    if (this.active < this.maxWorkers) {
      this.active++;
      return;
    }
    
    await new Promise(resolve => this.queue.push(resolve));
    this.active++;
  }
  
  release() {
    this.active--;
    const next = this.queue.shift();
    if (next) next();
  }
}

Adaptive intervals

// Reconciler speeds up when errors detected
if (consecutiveGreenSweeps >= 3) {
  adjustInterval(5 * 60 * 1000); // 5 minutes
} else {
  adjustInterval(1 * 60 * 1000); // 1 minute
}

Scope-based routing

function shouldDecompose(task: Task): boolean {
  return task.scope.length >= 4;  // 4+ files → subplanner
}

Next steps

Architecture

Deep dive into component architecture and design

Configuration

Configure workers, timeouts, and sandbox resources

Build docs developers (and LLMs) love