Skip to content

feat(server): thread groups live on the server with agent tools - #1840

Merged
donjor merged 9 commits into
hyprwsfrom
feat/server-thread-groups
Oct 25, 2026
Merged

donjor merged 9 commits into
hyprwsfrom
feat/server-thread-groups

Conversation

@donjor-agent

@donjor-agent donjor-agent Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

🤖 claude/anthropic claude-opus-5-5 (high)

Problem

Sidebar thread groups live only in each browser's local storage.

  • Agents: cannot read or edit groups
  • Launches: children land outside the group
  • Clients: a second browser or device sees none

Change

Groups now live per environment on the server.

  • Server: ThreadGroupsServiceFork
    • Table: fork_thread_groups
  • RPCs:
    • threadGroups.subscribeFork
    • threadGroups.mutateFork
  • Capability: optional threadGroupsFork
    • Upstream web and mobile clients ignore it
  • Reducer: one, shared in contracts
  • MCP tools:
    • t3_thread_group_list
    • t3_thread_group_update
  • Launch: joins the caller's group by default
    • group: an existing {groupId}, or "none"
    • New groups: t3_thread_group_update
    • Batch launches place each accepted thread
  • Web: follows the server, optimistic edits
    • Pending edits replay over newer snapshots
    • Replies older than the held revision drop
  • Collapsed state: stays per client
  • Legacy: local groups import once
    • A failed import retries

Known gap: launched threads still get parentThreadId: null; lineage is a separate follow-up.

Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

Verification

Isolated dev server on a seeded worktree .t3 home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

  1. Agent (GPT-6-Astra via Codex) groups 3 threads
  2. "Agent group" appears live on both clients
  3. Agent launches a child with no group option
  4. Child joins: "Agent group (4)" on both clients
thread-groups-two-clients.mp4

Host Chrome

c1-launch2.png

VM Firefox

c2-launch2.png

Checks

  • apps/server: vp run typecheck → exit 0
  • vp run fork:ci → exit 0
  • apps/server: vp run build:bundle → exit 0
  • apps/web: vp run build → exit 0
  • MCP registration test fails on old params
  • vp run check:changed → exit 1
    • 5005/5006 pass
    • Fails: GitManager "includes CLAUDE.md"
    • Cause: local global git config
    • Passes with GIT_CONFIG_GLOBAL=/dev/null

Claude Opus 5.5 via Claude Code in T3 Code.

Fork trailers

Fork-Domain: thread-ordering
Fork-Tier: qol

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Domain: thread-ordering
Fork-Tier: qol
@rsi-software-agent rsi-software-agent Bot added the size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). label Oct 25, 2026
@rsi-software-agent

rsi-software-agent Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

🧩 Effect Service Conventions fbd537a ✅ clear

resolved 7

commit 5e008e3

  • effect-service-conventions-1 Move launch-group capability into the fork service ✅ dd19a24
  • effect-service-conventions-2 Move effectful group enrichment out of MCP handlers ✅ 81ca197
  • effect-service-conventions-3 Give thread-group failures structured domain attributes ✅ dd19a24
  • effect-service-conventions-4 Use namespace imports at thread-group service boundaries ✅ dd19a24
  • effect-service-conventions-5 Export plain layer from the concrete service module ✅ dd19a24

commit dd19a24

  • effect-service-conventions-6 Split validation errors whose discriminator selects public messages ✅ 81ca197
  • effect-service-conventions-7 Preserve typed member-read failures when moving enrichment ✅ 81ca197

🕐 25 Oct 04:44 UTC ⏱️ 2 min 🤖 gpt-6.1-sol 🪙 610.3K

⚠️ Not verified: Tests and typechecks unrun: vp unavailable. git diff --check passed.

Details

effect-service-conventions-1 Move launch-group capability into the fork service · apps/server/src/mcp/toolkits/threadGroups/handlers.fork.ts:73

  • Problem: An exported transport helper resolves persisted groups and orchestrates membership writes.
  • Fix: Move resolution and joining into ThreadGroupsServiceFork methods; retain a thin fork adapter.
  • Note: prepareLaunch now owns destination resolution and membership writes inside the fork service.

effect-service-conventions-2 Move effectful group enrichment out of MCP handlers · apps/server/src/mcp/toolkits/threadGroups/handlers.fork.ts:25

  • Problem: MCP handlers load member shells and calculate described snapshots outside the service.
  • Fix: Expose described-list/update service methods; keep authorization and transport mapping in handlers.
  • Note: Handlers now call list and edit; the service owns snapshot retrieval, mutation, and enrichment.

effect-service-conventions-3 Give thread-group failures structured domain attributes · packages/contracts/src/threadGroups.fork.ts:89

  • Problem: ThreadGroupErrorFork stores only message text rather than structured validation attributes.
  • Fix: Return structured reasons and identifiers, deriving messages while preserving caller-visible wording.
  • Note: Structured reason and entity attributes replace the message-only domain error.

effect-service-conventions-4 Use namespace imports at thread-group service boundaries · apps/server/src/mcp/toolkits/threadGroups/handlers.fork.ts:13

  • Problem: New MCP and fork RPC consumers import the service tag by name.
  • Fix: Import the service module as a namespace in handlers, tools, and fork RPC wiring.
  • Note: The identified MCP and fork RPC consumers now import the service module as a namespace.

effect-service-conventions-5 Export plain layer from the concrete service module · apps/server/src/orchestration-v2/ThreadGroupsService.fork.ts:145

  • Problem: The single-implementation service exports threadGroupsServiceLiveFork instead of the required layer.
  • Fix: Rename the export to layer and update consumers through existing fork seams.
  • Note: The concrete service exports plain layer, and its consumers have been updated.

effect-service-conventions-6 Split validation errors whose discriminator selects public messages · packages/contracts/src/threadGroups.fork.ts:124

  • Problem: The reason discriminator selects caller-visible messages rather than providing diagnostic-only metadata.
  • Fix: Use separate tagged error classes and a union, preserving current messages.
  • Note: Separate tagged validation errors replace the reason discriminator while preserving public messages.

effect-service-conventions-7 Preserve typed member-read failures when moving enrichment · apps/server/src/orchestration-v2/ThreadGroupsService.fork.ts:176

  • Problem: Moved member lookups turn previously typed MCP failures into defects with Effect.orDie.
  • Fix: Keep read failures typed and map them to the existing MCP unavailable response.
  • Note: Member-read failures remain typed and map to the existing unavailable response in MCP handlers.
Head Result Time Check
fbd537a ✅ clear 2 min run
6908994 ✅ clear 2 min run
24ce29b ✅ clear 1 min run
81ca197 ✅ clear · 3 fixed 2 min run
dd19a24 🔴 3 open · 4 fixed · 1 held · 2 new 3 min run
5e008e3 🔴 5 open · 5 new 3 min run

@rsi-software-agent

rsi-software-agent Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

🧩 UI Consistency fbd537a ✅ clear

No findings.

🕐 25 Oct 04:46 UTC ⏱️ 2 min 🤖 gpt-6.1-sol 🪙 267.4K

Details
Head Result Time Check
fbd537a ✅ clear 2 min run
6908994 ✅ clear 2 min run
24ce29b ✅ clear 2 min run
81ca197 ✅ clear 1 min run
dd19a24 ✅ clear <1 min run
5e008e3 ✅ clear 2 min run

@rsi-software-agent

rsi-software-agent Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

🩺 Aftercare fbd537a ✅ clear

resolved 12

commit 5e008e3

  • A1 Preserve pending edits when applying server snapshots ✅ 24ce29b
  • A2 Make persistence and snapshot publication interruption-safe ✅ 81ca197
  • A3 Place each accepted child even when a later batch launch fails ➖ require-dependency part; each accepted child now joins as it is accepted (dd19a24), but 'serviceOption' stays: upstream…
  • A4 Represent group validation failures with structured domain attributes ✅ 81ca197
  • A5 Move effectful group enrichment into the fork service ✅ 81ca197
  • A6 Align the service export and consumers with service conventions ✅ 81ca197
  • A7 Retry pending legacy imports after transient failures ✅ 81ca197
  • A8 Gate grouping controls against the new environment constraints ✅ 6908994
  • A9 Resolve the promised new-group launch behavior ➖ Picked b: Obtain approval to narrow scope to caller/existing-group selection and opt-out.

commit 81ca197

  • A10 Place accepted children before fallible post-create work ✅ 6908994

commit 24ce29b

  • A11 Use the command scheduler instead of a feature-local promise queue ✅ 6908994

commit 6908994

  • A12 Restore the two-rename regression through real command scheduling ✅ fbd537a

🕐 25 Oct 04:49 UTC ⏱️ 3 min 🤖 gpt-6.1-sol 🪙 938.2K

⚠️ Not verified: Source-level review; live multi-client behavior, launch failure injection, and mutation proof not independently reprodu…

Details

A1 Preserve pending edits when applying server snapshots · apps/web/src/state/threadGroups.ts:195

  • Problem: Snapshots and older RPC replies overwrite pending edits and newer confirmed state.
  • Fix: Replay ordered pending mutations over authoritative snapshots; reject stale results.
  • Note: Edits serialize per environment; the two-rename source probe preserves the latest title.

A2 Make persistence and snapshot publication interruption-safe · apps/server/src/orchestration-v2/ThreadGroupsService.fork.ts:128

  • Problem: Cancellation after SQL commit can leave memory stale and lose committed edits.
  • Fix: Protect persistence through publication under the write lock; test commit-boundary cancellation.
  • Note: Persistence and snapshot publication now share one uninterruptible section.

A3 Place each accepted child even when a later batch launch fails · apps/server/src/mcp/toolkits/orchestrator/handlers.ts:106

  • Problem: Whole-batch success gating leaves accepted children ungrouped when later launches fail.
  • Fix: Join each accepted child through the fork service; require its dependency explicitly.
  • Note: require-dependency part; each accepted child now joins as it is accepted (dd19a24), but 'serviceOption' stays: upstream….

A4 Represent group validation failures with structured domain attributes · packages/contracts/src/threadGroups.fork.ts:89

  • Problem: Reducer and service failures expose only prose, preventing structured failure handling.
  • Fix: Return structured reasons and identifiers, deriving the existing caller-visible messages.
  • Note: Separate tagged errors require identifiers and derive the existing messages.

A5 Move effectful group enrichment into the fork service · apps/server/src/mcp/toolkits/threadGroups/handlers.fork.ts:25

  • Problem: MCP handlers own member loading, deleted-thread filtering, and described snapshot construction.
  • Fix: Expose described-list/update service operations; retain authorization and transport mapping in handlers.
  • Note: Tools call list/edit once; enrichment and typed member-read failures stay service-owned.

A6 Align the service export and consumers with service conventions · apps/server/src/orchestration-v2/ThreadGroupsService.fork.ts:145

  • Problem: The concrete layer export and named service imports violate required service conventions.
  • Fix: Export layer and use service module namespaces through existing fork hooks.
  • Note: The service exports layer and its consumers use module namespaces.

A7 Retry pending legacy imports after transient failures · apps/web/src/state/threadGroups.ts:321

  • Problem: A failed first import cannot retry on later snapshots or reconnects.
  • Fix: Track successful migration separately and retry pending imports after reconnection.
  • Note: Failed imports release their running marker and retry on subsequent snapshots.

A8 Gate grouping controls against the new environment constraints · apps/web/src/components/Sidebar.tsx:2203

  • Problem: Grouping controls offer unsupported operations, clear selection, and request unnecessary titles.
  • Fix: Gate controls by environment, capability, readiness, and permission before creation proceeds.
  • Note: Preview and release validate all members and destination; read-only header edits hide.

A9 Resolve the promised new-group launch behavior · packages/contracts/src/threadGroups.fork.ts:80

  • Problem: The PR promises new-group launches, but the launch schema accepts only existing groups.
  • Fix: Implement new-group launch semantics or obtain approval to narrow the advertised scope.
  • Note: Picked b: Obtain approval to narrow scope to caller/existing-group selection and opt-out.

A10 Place accepted children before fallible post-create work · apps/server/src/mcp/OrchestratorMcpService.ts:2674

  • Problem: Post-create failures bypass placement, leaving durable children outside their intended group.
  • Fix: Place immediately after successful thread.create; test failure before prompt dispatch completes.
  • Note: Both launch paths place accepted threads before fallible post-create work.

A11 Use the command scheduler instead of a feature-local promise queue · apps/web/src/state/threadGroups.ts:217

  • Problem: A private promise queue duplicates serial scheduling already supported by atom commands.
  • Fix: Configure serial concurrency on the mutation command and remove the private queue.
  • Note: Shared serial command scheduling replaces the private per-environment promise queue.

A12 Restore the two-rename regression through real command scheduling · apps/web/src/state/threadGroups.test.ts:89

  • Problem: The replacement test bypasses serialization and no longer catches the original rename race.
  • Fix: Exercise two pending renames through real command scheduling, mocking only transport.
  • Note: Two renames exercise the real command factory and scheduler, asserting serialized transport.
Head Result Time Check
fbd537a ✅ clear · 1 fixed 3 min run
6908994 🟡 1 open · 3 fixed · 1 new 5 min run
24ce29b 🔴 3 open · 1 fixed · 2 held · 1 new 7 min run
81ca197 🔴 3 open · 5 fixed · 2 held · 1 new 5 min run
5e008e3 🔴 9 open · 9 new 9 min run

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Upstreamable: no
@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

A9: b

The maintainer scoped launch placement as "in its group; another group; or in no group".
New groups come from t3_thread_group_update; the body now says so.

A3: drop require-dependency part; each accepted child now joins as it is accepted (dd19a24), but serviceOption stays: upstream launch-tool tests build those handlers without the fork service, and a required dependency would force fork edits into them.

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Upstreamable: no
@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

Fixed in 81ca197:

  • effect-service-conventions-2: list and edit are service methods; each tool calls one
  • effect-service-conventions-6: one error class per reason, unioned as ThreadGroupErrorFork
  • effect-service-conventions-7: member reads stay typed; tools map them to unavailable

@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

Requested a rerun for one exact failed or cancelled CI job on the unchanged pull-request head. GitHub may also rerun dependent jobs.

  • Head: 81ca19754c7bafe9f615d6a38d411267bd09e0be
  • Workflow: hyprws CI; run 40159716120; pre-rerun attempt 1; open run
  • Job: Test Web; id 121068530469; open job
  • Check run: 121068530469
  • Genuine flake reason: markdownPipeline.browser.test.ts Shiki highlight wait timed out; file untouched by this PR, passed on prior head dd19a24, passes locally

… unavailable

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Upstreamable: no
@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

Fixed in 24ce29b:

  • A1: edits send one at a time per environment; replies arrive in order (test fails without it)
  • A8: create, move, and drag need one environment that serves groups, plus operate scope
  • A10: placement runs right after thread.create, before prompt dispatch and timeline record

A10: drop test part: only the upstream createThreads harness reaches a post-create failure, and fork rules keep upstream test files unedited

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Upstreamable: no
… grouping is unavailable

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Upstreamable: no
@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

  • A11: client-runtime serial concurrency keyed by environment; feature queue deleted
  • A10: ThreadLaunchService.launch places right after its thread.create claim, before the initial message dispatch
  • A8: drag preview and release check every moved thread and the target group's environment; group headers hide edit actions without operate scope or a serving environment

@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

Requested a rerun for one exact failed or cancelled CI job on the unchanged pull-request head. GitHub may also rerun dependent jobs.

  • Head: 69089946e68228f94783af914c3d0ffa1e6c8e73
  • Workflow: hyprws CI; run 40161517377; pre-rerun attempt 1; open run
  • Job: Test Web; id 121073899128; open job
  • Check run: 121073899128
  • Genuine flake reason: markdownPipeline.browser.test.ts token highlight flake; PR touches no markdown code

…onment

Fork-Domain: thread-ordering
Fork-Tier: qol
Fork-Upstreamable: no
@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

A12: threadGroups.fork.test.ts sends two renames through the real scheduler

  • Mocked: only the RPC transport
  • Checks: the second waits for the first reply
  • Proof: fails without serial mode

@donjor-agent

donjor-agent Bot commented Oct 25, 2026

Copy link
Copy Markdown
Author

🤖 claude/anthropic claude-opus-5-5 (high)

Requested a rerun for one exact failed or cancelled CI job on the unchanged pull-request head. GitHub may also rerun dependent jobs.

  • Head: fbd537a50be98e4ad0bb006fe78ccb5843065e9e
  • Workflow: hyprws CI; run 40162868657; pre-rerun attempt 1; open run
  • Job: Test Web; id 121077898705; open job
  • Check run: 121077898705
  • Genuine flake reason: markdownPipeline.browser.test.ts Shiki highlight flake; same test passed on rerun of 6908994; PR touches no markdown code

@donjor
donjor merged commit 3c85627 into hyprws Oct 25, 2026
20 of 21 checks passed
@donjor
donjor deleted the feat/server-thread-groups branch October 25, 2026 04:58
donjor added a commit that referenced this pull request Oct 26, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
donjor added a commit that referenced this pull request Oct 27, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
donjor added a commit that referenced this pull request Oct 27, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Oct 30, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Oct 30, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Oct 31, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Oct 31, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
donjor added a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
donjor added a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 6, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 7, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 7, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 7, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 7, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 7, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 7, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 8, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 8, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 8, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 8, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 9, 2026
## Problem

Sidebar thread groups live only in each browser's local storage.

- **Agents:** cannot read or edit groups
- **Launches:** children land outside the group
- **Clients:** a second browser or device sees none

## Change

Groups now live per environment on the server.

- **Server:** `ThreadGroupsServiceFork`
  - Table: `fork_thread_groups`
- **RPCs:**
  - `threadGroups.subscribeFork`
  - `threadGroups.mutateFork`
- **Capability:** optional `threadGroupsFork`
  - Upstream web and mobile clients ignore it
- **Reducer:** one, shared in contracts
- **MCP tools:**
  - `t3_thread_group_list`
  - `t3_thread_group_update`
- **Launch:** joins the caller's group by default
  - `group`: an existing `{groupId}`, or `"none"`
  - New groups: `t3_thread_group_update`
  - Batch launches place each accepted thread
- **Web:** follows the server, optimistic edits
  - Pending edits replay over newer snapshots
  - Replies older than the held revision drop
- **Collapsed state:** stays per client
- **Legacy:** local groups import once
  - A failed import retries

Known gap: launched threads still get `parentThreadId: null`; lineage is a separate follow-up.

## Scope and approval

Fork-only feature, scoped by the maintainer in the T3 thread: per-environment groups, launch auto-join with opt-out, per-client collapse.

## Verification

Isolated dev server on a seeded worktree `.t3` home.
Client 1 is host Chrome; client 2 is Firefox in a QA VM over an SSH tunnel.

1. Agent (GPT-6-Astra via Codex) groups 3 threads
2. "Agent group" appears live on both clients
3. Agent launches a child with no `group` option
4. Child joins: "Agent group (4)" on both clients

https://github.com/user-attachments/assets/80208be1-44dd-4918-84ce-e3f11fef4a00

**Host Chrome**

![c1-launch2.png](https://github.com/user-attachments/assets/23920100-e333-47a9-9791-c8b3c980e707)

**VM Firefox**

![c2-launch2.png](https://github.com/user-attachments/assets/3236987b-8996-4add-a700-c2c3a55e4cc1)

**Checks**

- [x] `apps/server: vp run typecheck` → exit 0
- [x] `vp run fork:ci` → exit 0
- [x] `apps/server: vp run build:bundle` → exit 0
- [x] `apps/web: vp run build` → exit 0
- [x] MCP registration test fails on old params
- [ ] `vp run check:changed` → exit 1
  - 5005/5006 pass
  - Fails: GitManager "includes CLAUDE.md"
  - Cause: local global git config
  - Passes with `GIT_CONFIG_GLOBAL=/dev/null`

Claude Opus 5.5 via Claude Code in T3 Code.

## Fork trailers

Squashes:

- 01547bb feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- a4b48f8 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 50aba4d feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ef3cc72 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- ad6dbd0 feat(server): thread groups live on the server with agent tools (#1840) (#1840)
- 32d73f7 fixup! feat(server): thread groups live on the server with agent tools (#1840) (#1840)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
rsi-software-agent Bot pushed a commit that referenced this pull request Nov 9, 2026
## Problem

A group with a parent thread showed its title as a section header above the parent, detached from it.
Collapse lived in two places: the header chevron, and an `8 threads` fold strip under the parent.

## Change

- **Title tab:** joined to the parent card
- **One control:** the tab toggles the children
- **Collapsed:** urgent-first status dots, max 6 plus `+N`
- **Tab tooltip:** every child's status; opens on focus
- **Count:** children only
- **Fold strip:** deleted, with drag hook and test
- **Parentless groups:** header unchanged
- **Cross-project:** cards keep their project line

**Fork isolation:** the header lives in `SidebarThreadGroup.tsx`; upstream files carry `fork-hook` lines only.

## Scope and approval

The human picked concept A (Tab) from four captured concepts, relayed by the orchestrator thread.
Follows #1829 and #1840.

## Verification

Real Chrome, seeded dev data: a cross-project parent group (8 children, 6 projects) and a parentless group (3).

- Tab click flips `aria-expanded`; 6 dots plus `+2`
- Keyboard focus on the tab opens the status list
- Enter on the focused tab toggles; focus stays
- Double-click opens rename; hover menu opens
- Parentless header toggles as before
- Light theme and active parent checked
- No new console errors

| State | Before | After |
| --- | --- | --- |
| Expanded | ![before](https://github.com/user-attachments/assets/4a6c2a8d-befe-40b0-943d-6d9cdf62b6a5) | ![after](https://github.com/user-attachments/assets/e801aeb1-5cb4-4be9-ad1a-361d5e2ba9b3) |
| Collapsed | ![before](https://github.com/user-attachments/assets/8f9f9ccf-63d6-4589-a710-508a102df85f) | ![after](https://github.com/user-attachments/assets/f8338eca-d3a6-4d6b-a803-cc8599048714) |

**Active parent:** ![active](https://github.com/user-attachments/assets/08ed94ed-5806-4113-9f09-d66625d8d2b6)

**Checks:**

- `vp run typecheck` (apps/web) → exit 0
- `vp run check:changed` → exit 1 on the last commit, from the unrelated `markdownPipeline.browser.test.ts`; it passes alone (exit 0 on earlier commits)
- `vp run fork:ci` → exit 0

Claude Opus 5.5, Claude Code in T3 Code.

## Fork trailers

Squashes:

- 11840c4 feat(web): group title becomes a tab on its parent thread (#1878) (#1878)
- 960f0b5 fixup! feat(web): group title becomes a tab on its parent thread (#1878) (#1878)

Fork-Domain: thread-ordering
Fork-Tier: qol
Co-authored-by: donjor <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant