# ChatGPT Conversation ↔ Agent Session ↔ Worktree Ownership Specification

Status: proposed implementation contract  
Date: 2026-08-20  
Repository: `alexcodeplace/chatgpt-orchestrator`  
Related authority: `SPEC.md`

## 1. Purpose

Make it possible to answer, deterministically and without filesystem/process archaeology:

> Which ChatGPT conversation owns this worktree/branch, which execution session is currently allowed to mutate it, what plan/lane is it implementing, and what happened to that ownership after landing or handoff?

The seam must be infrastructure-owned. Agents must not be required to remember to maintain an `OWNER.md`, edit commit messages, or manually update mutable prose.

The orchestrator is the authoritative source for ChatGPT conversation identity because the WebExtension can observe the managed conversation URL/UUID. Git/worktree infrastructure remains authoritative for repository/worktree/branch identity. A durable binding joins the two through an execution-session identity.

## 2. Problem

Git can reliably answer:

- repository;
- worktree path;
- branch;
- HEAD;
- commit ancestry;
- remote preservation/landing state.

Git cannot natively answer:

- which ChatGPT conversation initiated/owns the work;
- which orchestrator worker/session is responsible;
- whether the owner is live, dead, handed off, or resumed;
- which plan/lane/request the worktree represents;
- which ChatGPT conversation ultimately produced a landed commit after the worktree is deleted.

Current process inference is insufficient because child processes may be reparented to systemd/tmux, work can resume in a new agent session, worktrees can be removed, and conversation identity is not guaranteed to appear in process arguments or Git history.

## 3. Design principles

1. **Separate identities.** A ChatGPT conversation, logical orchestrator worker, execution session, worktree, branch, and commit are different identities.
2. **Conversation identity is optional but never guessed.** Bind only a UUID/URL directly observed by the managed ChatGPT adapter or explicitly supplied through a trusted frontend seam.
3. **Execution-session identity is the primary live-owner key.** Conversation identity annotates/provenances the owner; it does not replace the execution-session/ledger identity.
4. **One live mutating owner per worktree.** Concurrent readers are allowed; conflicting mutation is denied while the current owner is live.
5. **History survives deletion.** Removing a worktree or branch must not destroy ownership provenance.
6. **No repository dirt.** Local ownership metadata must not require tracked files in the product repository.
7. **No secret material.** Conversation IDs/URLs are identifiers, not credentials. Authentication tokens, cookies, browser storage, API/site credentials, or chat bodies never enter ownership metadata.
8. **Fail closed on identity conflict; degrade honestly on missing identity.** Unknown conversation is represented as unknown, not inferred from title/cwd/process names.
9. **Landing closes the provenance chain.** A final receipt joins source worktree/branch/session/conversation to the landed commit.
10. **Frontend-independent core.** The protocol permits future executors/runtimes that have no ChatGPT conversation identity.

## 4. Identity model

```text
ChatGPT conversation (optional)
  conversation_id / conversation_url / title
                 |
                 | binds to
                 v
Logical orchestrator worker
  run_id / worker_id
                 |
                 | executed by one or more resumable sessions
                 v
Execution session / ledger
  execution_session_id / runtime / parent_session_id
                 |
                 | claims
                 v
Git worktree
  repo_id / worktree_path / branch
                 |
                 | produces
                 v
Source commit(s)
                 |
                 | submitted / landed
                 v
Landed commit / release receipt
```

### 4.1 Cardinality

- One ChatGPT conversation SHOULD map to one managed logical worker at a time.
- One logical worker MAY have multiple execution sessions over time because of restart/resume/handoff.
- One execution session MAY own multiple worktrees, but each worktree has at most one live mutating owner.
- One worktree MAY have multiple historical owners, represented as ordered ownership events.
- A conversation MAY outlive any local worktree.
- Non-ChatGPT runtimes have `conversation = null`.

## 5. Canonical conversation identity

The WebExtension already owns the ChatGPT-Web executor boundary and can observe the managed conversation URL after ChatGPT assigns one.

Canonical shape:

```ts
type ConversationIdentity = {
  provider: 'chatgpt-web';
  conversationId: string;      // canonical UUID parsed from /c/<uuid>
  conversationUrl: string;     // canonical https://chatgpt.com/c/<uuid>
  title?: string;              // informational only; never an identity key
  observedAt: string;          // ISO-8601
};
```

Rules:

- `conversationId` MUST be obtained from the managed browser surface, not reconstructed from prompt/title.
- The extension MUST NOT inspect unrelated tabs to discover ownership.
- Title is mutable and non-authoritative.
- URL must normalize to the same UUID as `conversationId`.
- An MCP/client request that does not carry a documented conversation UUID is not proof of conversation identity.

## 6. Orchestrator protocol additions

### 6.1 Worker execution binding

Extend the worker execution binding with optional conversation identity and execution provenance:

```ts
type ExecutionBinding = {
  executor: 'chatgpt-web' | string;
  executorInstanceId: string;
  executionSessionId?: string;
  conversation?: ConversationIdentity;
};
```

`conversation.created` / `conversation.bound` events persist the canonical identity once observed.

### 6.2 Ownership binding API

Add a narrow MCP/control-plane operation, name subject to the existing tool naming convention:

```text
ownership.bind
```

Input:

```ts
{
  runId?: string;
  workerId?: string;
  executionSessionId: string;
  repoRoot: string;
  worktreePath: string;
  branch: string;
  lane?: string;
  planRef?: string;
  requestId?: string;
}
```

The server fills conversation identity from the authenticated worker/executor binding. Callers MUST NOT be allowed to assert an arbitrary ChatGPT conversation UUID for another worker.

Response returns the durable ownership record ID and current normalized binding.

### 6.3 Lookup API

Provide read-only lookups:

```text
ownership.get(worktreePath | branch | ownershipId)
ownership.list(repoRoot?, state?)
ownership.history(worktreePath | branch | landedCommit)
```

These are suitable for an Overdeck `od-worktree who/list --owners` adapter without coupling Git infrastructure to ChatGPT DOM code.

## 7. Durable ownership record

Canonical current projection:

```json
{
  "schemaVersion": 1,
  "ownershipId": "own_...",
  "repoRoot": "/home/user/Projects/example",
  "worktreePath": "/home/user/Projects/example/.worktrees/feature-x",
  "branch": "wt/feature-x",
  "lane": "feature-x",
  "planRef": "docs/plans/feature-x.md",
  "requestId": null,
  "worker": {
    "runId": "run_...",
    "workerId": "worker_..."
  },
  "owner": {
    "executionSessionId": "session_...",
    "runtime": "chatgpt-web",
    "parentExecutionSessionId": null,
    "conversation": {
      "provider": "chatgpt-web",
      "conversationId": "00000000-0000-4000-8000-000000000000",
      "conversationUrl": "https://chatgpt.com/c/00000000-0000-4000-8000-000000000000",
      "title": "Example lane",
      "observedAt": "2026-08-20T00:00:00Z"
    }
  },
  "state": "active",
  "claimedAt": "2026-08-20T00:00:00Z",
  "lastSeenAt": "2026-08-20T00:01:00Z"
}
```

The projection is derived from an append-only event stream or transactional event table. It is not the only historical copy.

## 8. Ownership lifecycle/events

Required events:

- `ownership.claimed`
- `ownership.conversation_bound`
- `ownership.plan_bound`
- `ownership.heartbeat`
- `ownership.handoff_started`
- `ownership.released`
- `ownership.reclaimed`
- `ownership.wip_preserved`
- `ownership.submitted`
- `ownership.landed`
- `ownership.worktree_removed`

Every event includes `ownershipId`, timestamp, actor/execution session, repository/worktree/branch identity as applicable, and a monotonic sequence/cursor.

A handoff must preserve both old and new owner identities. Reclaiming a dead owner must append a new event; it must not rewrite history.

## 9. Local Git/worktree projection

The orchestrator does not replace Git. The Git-side integration SHOULD maintain a fast local projection in worktree-specific Git config:

```text
overdeck.owner.ownership-id
overdeck.owner.execution-session-id
overdeck.owner.runtime
overdeck.owner.conversation-id
overdeck.owner.conversation-url
overdeck.owner.worker-id
overdeck.owner.run-id
overdeck.lane
overdeck.plan-ref
```

Requirements:

- use `extensions.worktreeConfig` + `git config --worktree` where supported;
- metadata MUST NOT appear in product `git status`;
- absence of a local projection does not erase durable orchestrator history;
- a local projection that conflicts with durable ownership is treated as stale/corrupt and repaired only through an explicit reconciliation path.

## 10. Live mutation lock integration

Existing worktree mutation locks SHOULD be enriched with stable ownership IDs rather than maintaining a separate concept.

Example local lock projection:

```json
{
  "schema_version": 2,
  "ownership_id": "own_...",
  "execution_session_id": "session_...",
  "conversation_id": "00000000-0000-4000-8000-000000000000",
  "claimed_at": 1787190000,
  "last_seen_at": 1787190060
}
```

The lock answers **who may mutate now**. The durable ownership registry answers **who owned this over time**.

A dead-session reclaim:

1. proves the current owner session is no longer live according to the execution-session authority;
2. appends `ownership.released`/`ownership.reclaimed` history;
3. atomically updates the live lock/projection;
4. never deletes prior provenance.

## 11. Landing provenance

The landing controller receives or resolves `ownershipId` and writes a durable landing event/receipt:

```json
{
  "ownershipId": "own_...",
  "sourceBranch": "wt/feature-x",
  "sourceHead": "abc123...",
  "landedBranch": "main",
  "landedHead": "def456...",
  "landedAt": "2026-08-20T00:30:00Z"
}
```

This makes a landed commit queryable back to:

```text
landed commit -> source branch/worktree -> execution session -> worker -> ChatGPT conversation
```

The implementation SHOULD NOT inject conversation UUIDs into ordinary commit subjects/bodies. Optional Git notes/trailers may be evaluated later, but the control-plane receipt is canonical.

## 12. CLI/UX contract for consumers

The orchestrator exposes structured data; an Overdeck adapter can render:

```text
od-worktree who feature-x
```

Expected human-facing information:

```text
Worktree:      .../.worktrees/feature-x
Branch:        wt/feature-x
State:         ACTIVE
Lane:          feature-x
Plan:          docs/plans/feature-x.md
Owner runtime: ChatGPT Web
Conversation:  Example lane
Conversation:  https://chatgpt.com/c/<uuid>
Worker:        /root/feature-x
Session:       session_...
Last activity: ...
Latest WIP:    origin/wip/archive/...
```

`od-worktree list --owners` SHOULD expose a compact tabular projection. Unknown values display `unknown`, never inferred guesses.

## 13. Security and privacy

- Never store ChatGPT cookies, access tokens, MCP bearer tokens, site credentials, prompt bodies, or conversation transcripts in ownership records.
- Treat conversation URL/UUID as metadata that may be sensitive in logs; redact by policy when exporting diagnostics outside the local trusted control plane.
- Ownership writes require an authenticated orchestrator worker/executor/session context.
- A worker cannot bind itself to another worker's conversation.
- Repository/worktree paths must be normalized and validated against configured allowed roots before any Git-side projection write.
- Read APIs may expose less detail to untrusted clients than the local owner/admin view.

## 14. Reliability/reconciliation

### 14.1 Session dies with dirty worktree

Keep ownership history and local Git projection. Mark live owner stale only after session liveness proof. A successor records a handoff/reclaim event before mutation.

### 14.2 Worktree deleted but branch survives

History remains queryable by branch/ownership ID. Recreated worktree may rehydrate the projection from durable state.

### 14.3 Branch renamed

Append branch-binding change; do not create a new ownership record solely for rename.

### 14.4 Conversation unavailable

Continue with worker + execution-session ownership and `conversation: null`. Never fabricate a UUID.

### 14.5 Orchestrator unavailable during local worktree creation

Local Git integration may create a provisional session-owned projection with a generated idempotency key, then reconcile once the orchestrator returns. It must not invent conversation identity.

### 14.6 Duplicate/replayed bind

Binding is idempotent on `(executionSessionId, normalized repoRoot, normalized worktreePath)` while active. Retries return the same ownership record unless an explicit handoff/reclaim occurred.

## 15. Observability

Expose structured state suitable for AWP/Overdeck UI:

- active worktrees by project;
- owner conversation title/link where available;
- live/stale/unowned state;
- branch/head/ahead-behind data supplied by Git-side adapter;
- plan/lane/request linkage;
- last activity;
- WIP preservation receipt;
- submitted/landed state;
- handoff lineage.

This allows the system to answer "who is doing this?" without inspecting arbitrary processes or chat content.

## 16. Non-goals

- storing conversation transcripts in Git metadata;
- forcing all executors to have a ChatGPT conversation;
- using chat title as identity;
- using process ancestry as the canonical owner;
- putting owner files into every product repository;
- adding conversation UUIDs to every commit message;
- replacing Git branches/worktrees with orchestrator state;
- silently stealing a live worktree from another session.

## 17. Acceptance criteria

1. A managed ChatGPT worker creates a worktree; `ownership.get` returns the exact observed ChatGPT conversation UUID/URL, logical worker, session, branch, lane, and plan.
2. A second live session attempting mutation is rejected by the Git-side ownership/lock adapter.
3. The original session dies; a successor can explicitly reclaim/handoff while preserving the original owner in history.
4. Worktree removal does not erase lookup by branch or landed commit.
5. Landing produces a receipt that resolves a main/master commit back to the source conversation.
6. A non-ChatGPT executor works with `conversation = null`.
7. Replayed bind/heartbeat/landing calls are idempotent.
8. No ownership record contains credentials, cookies, chat body text, or arbitrary transcripts.
9. Unknown conversation identity is rendered as unknown rather than inferred.
10. All DOM-derived conversation data comes only from managed/explicitly attached ChatGPT tabs.
