Deleted thread keeps its pull-request link, making the PR permanently unopenable
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Summary
A thread's pull-request link outlives the thread. Once the thread is deleted, the row in projection_thread_pull_requests remains and continues to be the resolution target for that PR, so the PR becomes permanently unopenable: every attempt resolves to the deleted thread, refreshes its linked_at, and never creates a replacement.
Recovered by deleting the orphaned row by hand. Nothing in the UI offered a way out.
Environment
- T3 Code (Alpha) 0.0.42,
serverVersion0.0.42, darwin/arm64 - Desktop-managed server
- Client: a local app embedding T3 in an iframe and driving it through
/api/orchestration/shelland/api/orchestration/dispatch
What happens
A thread linked to a PR was deleted at 15:20:06Z. For the next eight hours, every attempt to open that PR resolved to the deleted thread:
projection_thread_pull_requests
thread_id d261fce4-… ← projection_threads.deleted_at = 2026-09-17T15:20:06.731Z
repository <org>/<repo>
number <N>
source manual
linked_at 2026-09-17T23:49:44.494Z ← refreshed by the most recent attempt, 8h after deletion
It is the only row for that PR, and no replacement thread was ever created — projection_threads has no new row for that project across 90 minutes of repeated attempts, while every other PR in the same project has a live thread.
The git side succeeds. Server traces for the 23:49:44Z attempt show GitVcsDriver.remoteExists, fetchPullRequestHeadCommit, and resolveCommit all exiting Success, finishing 82ms before linked_at was refreshed. So the PR head is fetched and resolved, and the work is then attributed to a thread that no longer exists.
Expected
Any one of these would have prevented it:
- Deleting a thread clears (or tombstones) its rows in
projection_thread_pull_requests. - PR-thread preparation skips a link whose thread is deleted and creates a new thread instead, rather than resolving to the deleted one and refreshing its
linked_at. /api/orchestration/shellexposes deletion, so a client can tell. The thread shape currently carriessettledAt,snoozedUntil,archivedAt-adjacent state but nothing for deletion, so a client that receives a deleted thread cannot distinguish it from a live one. Filtering deleted threads out of the shell would work equally well; exposing the field lets clients report it.
Item 3 is what made this undiagnosable from outside: our client added a liveness check — "is this thread still in the shell?" — and it passed, because the deleted thread was indistinguishable from a live one in the projection the client can see.
Reproduction
- Open a pull request in T3 so a thread is created and linked to it.
- Delete that thread.
- Open the same pull request again.
Expected: a new thread. Actual: resolution to the deleted thread, linked_at refreshed, no new thread, nothing openable.
Workaround
delete from projection_thread_pull_requests
where repository='<org>/<repo>' and number=<N>;
Restart T3 afterwards. Identifiers for the affected private repository are redacted; thread UUIDs and timestamps are verbatim from the local state.sqlite on the affected machine.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing PR-thread preparation and deletion handling around projection_thread_pull_requests, then inspect the /api/orchestration/shell and /api/orchestration/dispatch entry points described in the report. Reproduce the sequence in the issue and inspect state.sqlite if needed. Done means reopening a PR after its thread is deleted no longer resolves to that deleted thread, and the shell behavior matches the chosen fix.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100