# How Ego-Browser Helpers Handle User-Owned Task Spaces: Skip Logic and Claim Workflows

> Discover how ego-browser helpers manage user owned task spaces with skip logic and claim workflows. Learn to avoid unintended mutations.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-29

---

**Ego-browser helpers automatically skip mutating actions on user-owned task spaces, returning `{ done: false, skipped: "user-owned" }` unless you explicitly claim the space first.**

When building autonomous browsing workflows with **ego-browser**, understanding how **task space ownership** affects helper behavior is critical for reliable agent-user collaboration. The `citrolabs/ego-lite` repository implements a strict protection model: user-owned contexts are immutable by default, preventing accidental interference while preserving explicit control.

---

## Understanding Task Space Ownership Levels

Every task space in ego-browser carries an `ownership` field with three possible values:

- **`'agent'`** — Fully controlled by the agent; all helpers operate normally
- **`'agentDelegatedToUser'`** — Agent-initiated but user-visible; partial restrictions apply
- **`'user'`** — User-created and user-controlled; mutating helpers are blocked

This ownership model is documented in [`skills/ego-browser/SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md):

> "Every task space has `ownership: 'agent' | 'agentDelegatedToUser' | 'user'`; the helpers treat user‑owned spaces differently."【/SKILL.md#L75-L82】

---

## Helper Behavior When Targeting User-Owned Spaces

The skip logic is centralized in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts). Here's how each helper responds:

| Helper | User-Owned Behavior |
|--------|---------------------|
| `handOffTaskSpace` | **Skipped** — resolves to `{ done: false, skipped: "user-owned" }`【/helpers.ts#L123-L128】 |
| `completeTaskSpace(..., { keep: true })` | **Skipped** — same skip result as above【/helpers.ts#L123-L128】 |
| `claimTaskSpace` | **Allowed** — transfers ownership to agent, enabling subsequent operations【/helpers.ts#L206-L218】 |
| `useOrCreateTaskSpace` | **Selects without claiming** — does NOT auto-transfer ownership; space remains user-owned【/helpers.ts#L123-L128】 |
| `handOffTaskSpace` (post-claim) | **Works normally** — only if ownership was successfully changed; otherwise still skipped【/helpers.ts#L298-L324】 |

The implementation uses a consistent check pattern. In [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts), the core skip condition evaluates whether `space.ownership === 'user'` before executing mutating operations.

---

## The Claim-First Workflow

To modify a user-owned task space, you **must explicitly claim it** first. According to [`SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/SKILL.md):

> "To work with a user‑owned space you must **first claim it** (e.g., `await claimTaskSpace(id)`) before performing any modifying actions. After claiming, the space's ownership changes to the agent, and other helpers operate normally."【/SKILL.md#L73-L77】

This design ensures intentional handoffs rather than silent takeovers.

---

## Practical Code Examples

### Detecting and Handling User-Owned Spaces

```javascript
// List all task spaces and identify a user-owned one
const spaces = await taskSpaces.list();
const userSpace = spaces.find(s => s.ownership === 'user');

// Attempt handoff — will be skipped with diagnostic result
const handoff = await taskSpaces.handOff(userSpace.id);
console.log(handoff); // { done: false, skipped: 'user-owned' }

// Claim transfers ownership to agent
await taskSpaces.claim(userSpace.id);

// Now mutating operations succeed
await taskSpaces.handOff(userSpace.id);                    // works
await taskSpaces.complete(userSpace.id, { keep: true });   // works

```

### useOrCreateTaskSpace Limitations

```javascript
// useOrCreateTaskSpace does NOT auto-claim user-owned spaces
const ts = await taskSpaces.useOrCreate('my-research');

// If 'my-research' exists and is user-owned:
// - ts is selected and returned
// - ownership remains 'user'
// - handOff/complete will still skip until you claim()

// Required explicit claim for full control
if (ts.ownership === 'user') {
  await taskSpaces.claim(ts.id);
}

```

---

## Why This Protection Model Matters

The **skip-not-fail** approach provides several advantages:

1. **Non-destructive defaults** — Agents cannot accidentally disrupt user work
2. **Observable behavior** — The `skipped: "user-owned"` return value enables programmatic detection
3. **Explicit consent** — The `claimTaskSpace` requirement acts as an intentional handoff signal
4. **Audit trail** — Ownership changes are traceable in space history

This aligns with the broader ego-browser philosophy described in [`AGENTS.md`](https://github.com/citrolabs/ego-lite/blob/main/AGENTS.md), which emphasizes clear boundaries between autonomous agent actions and user-controlled contexts【/AGENTS.md#L27-L28】.

---

## Summary

- **User-owned spaces block mutating helpers** — `handOffTaskSpace` and `completeTaskSpace` return `{ done: false, skipped: "user-owned" }` per [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts) lines 123-128 and 298-324
- **`claimTaskSpace` is the gateway** — Explicitly transfers ownership from `'user'` to `'agent'`, enabling normal helper operation
- **`useOrCreateTaskSpace` is conservative** — Selects existing spaces without claiming, preserving user ownership
- **Check `ownership` before acting** — Query `taskSpaces.list()` or inspect the `ownership` field on any space object to predict helper behavior

---

## Frequently Asked Questions

### Why does `handOffTaskSpace` skip instead of throwing an error?

The ego-browser design favors **graceful degradation** over exceptions. Returning `{ done: false, skipped: "user-owned" }` allows agents to detect the condition programmatically and decide whether to claim, notify the user, or proceed with read-only operations. This pattern appears consistently across [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts) lines 123-128 and 298-324.

### Can I override the skip behavior without claiming?

**No.** The source code in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) implements ownership checks without configuration overrides. This is intentional — the only supported path to modify a user-owned space is through `claimTaskSpace`, which updates the ownership field server-side before subsequent operations proceed.

### What happens if I call `claimTaskSpace` on an already-claimed space?

According to the implementation at [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts) lines 206-218, `claimTaskSpace` is **idempotent when called by the owning agent** — it succeeds and selects the space. If another agent owns the space, the claim will fail with an ownership conflict error.

### How do I detect if a space will trigger skip behavior before calling helpers?

Inspect the `ownership` property directly on any task space object:

```javascript
const space = await taskSpaces.get(id);
if (space.ownership === 'user') {
  // Mutating helpers will skip; claim first if modification is needed
  await taskSpaces.claim(id);
}

```

This check matches the internal logic used by the helpers themselves.