[Bug]: Clock correction inserts new messages above the thread start and pins the old task at the bottom
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Run T3 Code Desktop and its local T3 server on the same Windows machine.
- Put the Windows system clock several hours ahead. In the observed case, it was approximately 11 hours ahead.
- Start a Codex turn that runs long enough to emit messages and tool activities.
- While the turn is still active, correct the Windows clock.
- Allow the original turn to finish or interrupt it.
- Send another prompt in the same T3 thread.
- Repeat with a few short prompts, then reload or reopen the thread.
In the observed thread, the clock correction occurred between two consecutive persisted events:
Last event before correction
UTC: 2026-09-05T04:18:50.462Z
Dubai time: 2026-09-05 08:18:50.462 +04:00
Event seq: 272
First event after correction
UTC: 2026-09-04T17:18:56.456Z
Dubai time: 2026-09-04 21:18:56.456 +04:00
Event seq: 273
Clock moved backward by 10:59:54.006.
The same Codex turn started before the correction and reached a terminal state after it:
requested_at: 2026-09-05T04:16:02.417Z
started_at: 2026-09-05T04:16:02.417Z
state: interrupted
completed_at: 2026-09-04T17:19:14.370Z
This differs from #3983. That report covered a client clock that disagreed with a correct remote server clock. This case uses the desktop client and local T3 server on the same Windows host. Correcting the host clock changes the clock used by both sides, so assigning timestamps on the server does not prevent the regression.
This was discovered during a Convex CLI device-login attempt. The user approved the login in the browser, but Convex correctly rejected the returned token with RPError: JWT expired. The agent reported that the PC clock appeared to be about 10 hours ahead of Convex's issuer. That authentication failure exposed the incorrect Windows clock.
Convex did not corrupt the T3 thread. Its JWT expiry validation revealed the clock problem. The T3 failure began when the Windows clock was corrected while the same Codex turn was still active. Because the desktop client and local T3 server shared that host clock, the next persisted T3 event moved backward by 10:59:54.006.
Expected behavior
The original turn should settle when it completes or is interrupted. Prompts sent after the clock correction should append after the original conversation and become the current task. They should never be inserted above the thread's original first turn.
Changing the host wall clock should not make a completed turn look active. T3 should preserve event order across a backward clock adjustment and should never calculate a negative working duration.
Actual behavior
The original task remains at the bottom because its messages and tool activities have future timestamps. New prompts start and complete real Codex turns, but their corrected timestamps are earlier than the thread's original creation time. T3 therefore inserts every new prompt and response near the top, before the conversation's original first turn.
The corrected-time turns stay chronological relative to each other. They accumulate as an earlier block at the top of the thread. The entire block remains above the future-dated original conversation, while the old task stays at the bottom and continues to look current.
The result is especially confusing:
- New user messages and their assistant responses appear above the thread's original starting point instead of at the bottom.
- T3 keeps showing the old task at the bottom as if it were the current task.
- One activity header reads
Working for 0s. - Another affected activity reads
Worked for 659m 53s, matching the roughly 11-hour clock regression. - The stop control can appear while the displayed task is already terminal.
- Sending a new message looks like it resumed the old task, even though T3 created a separate turn.
- Restarting T3 does not repair the thread.
- Updating to the latest nightly does not repair timestamps already stored in
state.sqlite. - A user can keep sending prompts to find out what happened. Each prompt creates a real provider turn and may consume paid Codex usage while the UI continues to show the wrong task.
The screenshots supplied during diagnosis show both sides of the ordering failure. One view shows corrected-time prompts grouped near the top under Worked for 659m 53s. Other views show later What happened prompts and completed responses inserted above the original future-dated task. The original task remains at the bottom with its old tool history, Thinking, and Working for 0s.
Impact
Major degradation or frequent failure
Version or commit
T3 Code Nightly 0.0.39-nightly.20260904.1280
Environment
- Lenovo 83GS, x64
- 12th Gen Intel Core i5-12600HX
- 27.8 GB RAM
- Microsoft Windows 11 Pro, 64-bit
- Windows version
10.0.26200, build26200 - Time zone: Arabian Standard Time, UTC+04:00
- T3 Code Desktop Nightly
0.0.39-nightly.20260904.1280 - T3 desktop client and local T3 server running on the same machine
- Codex
0.153.2 - Provider: Codex
- Model shown in the thread: GPT-5.6 Sol
- Local checkout mode with full access
- Windows Time service was not running when checked after the incident.
w32tm /query /statusreturned0x80070426.
Logs or stack traces
Sanitized state.sqlite findings:
projection_turns
- Every stored turn was terminal: completed or interrupted.
- No turn in the affected thread remained active.
- One turn completed_at value was earlier than its requested_at and started_at values.
projection_threads
- latest_turn_id pointed to the genuinely newest completed turn.
- latest_user_message_at remained set to the future-dated prompt.
- updated_at was earlier than created_at.
projection_thread_messages
- New user and assistant messages used the corrected clock.
- The older task's messages retained future timestamps.
- Sorting by created_at placed the older task after the newer turns.
projection_thread_activities
- Clock-skewed activities remained future-dated through:
2026-09-05T04:18:50.462Z
- Later activities used corrected timestamps beginning at:
2026-09-04T17:18:56.456Z
orchestration_events
- Global sequence 272 occurred at 2026-09-05T04:18:50.462Z.
- Global sequence 273 occurred at 2026-09-04T17:18:56.456Z.
- The sequence numbers preserve the true ingestion order even though occurred_at moved backward.
No raw logs are included because they contain local paths, a production hostname, and expired device-authorization codes.
Screenshots, recordings, or supporting files
The existing sanitized screenshot shows the original future-dated task pinned at the bottom with Working for 0s.
Additional screenshots captured the corrected-time messages accumulating above the thread's original first turn, including the impossible Worked for 659m 53s duration. Private project names, thread names, authorization URLs, and expired device codes were redacted from those copies before upload.
Workaround
Starting a new T3 thread avoids the corrupted timeline. In the affected thread, newly submitted turns continue to be inserted above the original conversation until the real clock passes the future timestamps.
The affected thread begins ordering normally only after the real clock passes its largest persisted future timestamp. Restarting or updating T3 does not fix the stored records.
For prevention, enable Windows automatic time synchronization before starting provider work. This does not repair an existing affected thread.
A durable fix should use persisted event sequence for timeline order and active-state reconciliation. T3 should also detect a large backward wall-clock jump, clamp elapsed-time calculations, and provide a repair path for timestamps already stored out of order.
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 with the apps/server persistence represented by state.sqlite, especially projection_thread_messages, projection_thread_activities, projection_turns, projection_threads, and orchestration_events. Compare timeline ordering and active-state reconciliation using persisted sequence versus occurred_at, including the negative-duration case. Done means backward clock jumps no longer reorder turns or leave terminal work appearing active, with a repair path for already stored records.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- sqlite, typescript
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100