# T3ex Fork Thread

> Continue an existing T3 Code thread in the current session, optionally cutting off everything from a given message onward. Use when the user supplies a T3 Code thread id and wants to resume, fork, retry, or hand that conversation to a different model.

- Skill: `maddada/t3ex-fork-thread` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add maddada/t3ex-fork-thread`
- Raw SKILL.md: https://api.skillmd.com/api/skills/maddada/t3ex-fork-thread/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: maddada (https://skillmd.com/u/maddada)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/maddada/t3ex-fork-thread

---


# Fork a T3 Code Thread

Load a past T3 Code conversation into this session so you can carry on with it —
usually under a different model than the one that ran it.

T3 Code has no CLI or API for posting into an existing thread. Forking therefore
means *reading* the old thread and continuing the work here, in a new one. The
original is never modified; the extractor opens the database read-only.

## Invocation

```
/t3ex-fork-thread <thread-id> [time] [through] [instruction…]
```

The user copies the thread id from the T3 Code sidebar. A unique prefix is
enough — `ce419dc7` works as well as the full UUID.

## Run the extractor

`extract.py` sits next to this file. Run it from this skill's directory:

```bash
python3 extract.py <thread-id> [--at HH:MM[:SS]] [--through] [--turn N]
```

Python 3.10+, no dependencies.

| Flag | Effect |
| --- | --- |
| `--at HH:MM` | Local wall-clock time of a user message, exactly as the sidebar shows it |
| `--through` | Keep the message at `--at` instead of dropping it |
| `--turn N` | Keep the first N turns, ignoring `--at` |
| `--list-turns` | Print the numbered turn list and exit |
| `--output T.N` | Print one withheld command output, e.g. `--output 1.3` |
| `--db PATH` | Point at a specific `state.sqlite` |
| `--cwd PATH` | Directory to compare against the thread's repo (default: current) |
| `--max-diff-lines N` | Cap per-file diff lines (default 40) |

## Where the cut falls

**A bare time drops that message and everything after it.** The user is pointing
at where the thread went wrong, so the message they name is excluded.

```
/t3ex-fork-thread ce419dc7 07:58     →  extract.py ce419dc7 --at 07:58
```

If they say **through**, **keep**, or **including**, the named message is kept
and only what follows is dropped:

```
/t3ex-fork-thread ce419dc7 through 07:58  →  extract.py ce419dc7 --at 07:58 --through
```

With no time at all, the whole thread loads.

Times are local, matching the sidebar. The script converts to UTC itself — never
hand it a UTC time.

## When a minute is ambiguous

Two user messages can share a minute. The script then exits **2** and lists the
candidates rather than guessing:

```
error: 2 user messages share the minute 07:58:
  turn 2  07:58:45  add a command in root of this repo to run the new pr…
  turn 3  07:58:54  or npm run dev
Re-run with seconds (--at HH:MM:SS) or with --turn N.
```

Show the user those candidates and ask which one they meant. Do not pick for
them.

## What to do once it is loaded

**Thread id alone** — read the transcript, then give a short orientation: what
was done, what state the repo is in, what was left open. Stop there and wait.
The user is checking that you picked up the thread correctly before you touch
anything.

**Thread id plus an instruction** — load the context, then act on the
instruction in the same turn.

## Before you edit anything

The header prints the thread's repo and your current directory. If they differ
it says so loudly:

```
repo     /Users/madda/dev/active/Maestro3
cwd      /Users/madda/dev/active/t3code
         *** WORKING DIRECTORY DIFFERS FROM THE THREAD'S REPO ***
```

Reading a thread from another repo is fine. **Editing** is not — stop and
confirm with the user first. Applying a Maestro3 change while sitting in t3code
is the failure this guards against.

## What is included

Full conversation text, plus a one-line trace per tool call — the command that
ran, its exit status, and any file diffs. Captured stdout is withheld and its
size reported, because it dominates the data: a thread with 1.1 MB of raw
activity extracts to about 39 KB.

Pull a specific withheld output only when it actually matters:

```bash
python3 extract.py <thread-id> --output 1.3
```

## Escape hatch — the schema

For anything the script does not cover, query the database directly. It lives at
`~/.t3/userdata/state.sqlite` (`~/.t3/dev/userdata/state.sqlite` for a dev
home). Open it read-only.

| Table | Columns that matter |
| --- | --- |
| `projection_threads` | `thread_id`, `title`, `project_id`, `model_selection_json`, `fork_source_thread_id`, `deleted_at` |
| `projection_projects` | `project_id`, `title`, `workspace_root` |
| `projection_turns` | `turn_id`, `pending_message_id`, `assistant_message_id`, `requested_at`, `checkpoint_ref` |
| `projection_thread_messages` | `message_id`, `turn_id`, `role`, `text`, `created_at` |
| `projection_thread_activities` | `turn_id`, `kind`, `summary`, `payload_json`, `created_at` |

Four traps worth knowing before you write your own query:

- **User messages have a NULL `turn_id`.** They attach only through
  `projection_turns.pending_message_id`. Grouping on `turn_id` silently loses
  every user prompt.
- **`sequence` is NULL on every activity row.** Order by `created_at` only.
- **One assistant reply is often several rows.** Streamed chunks share a
  `turn_id`; join them in timestamp order.
- **`assistant_message_id` can dangle.** An interrupted turn points at a message
  row that does not exist.

Timestamps are ISO-8601 UTC. `payload_json` shapes vary by
`json_extract(payload_json, '$.itemType')`: `command_execution`, `file_change`,
`dynamic_tool_call`, `mcp_tool_call`, `web_search`, `image_view`.

Treat every projection table as read-only. The event log
(`orchestration_events`) is the source of truth and writing to projections
corrupts state.

## Related

`madda-search-conversation-history` covers the file-based side — Codex and
Claude `.jsonl` transcripts. Use that when there is no T3 Code thread id.

