Before submitting
Area
apps/web
Steps to reproduce
-
Create a Git repository where AGENTS.md is a regular tracked file and CLAUDE.md is another regular tracked file:
git init t3-codeview-type-change
cd t3-codeview-type-change
git config user.name "T3 repro"
git config user.email "t3-repro@example.invalid"
printf 'canonical instructions\n' > CLAUDE.md
printf 'duplicated instructions\n' > AGENTS.md
git add AGENTS.md CLAUDE.md
git commit -m baseline
-
Open the repository in T3 Code and start a thread.
-
During a turn, replace the regular file with a relative symlink and commit it:
rm AGENTS.md
ln -s CLAUDE.md AGENTS.md
git add AGENTS.md
git commit -m 'use shared instructions'
-
After the turn checkpoint is created, open the turn diff or Changes view.
The checkpoint transition is a valid Git type change:
mode change 100644 => 120000 AGENTS.md
With git diff --no-renames, Git represents it as two file blocks with the same path:
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
--- a/AGENTS.md
+++ /dev/null
diff --git a/AGENTS.md b/AGENTS.md
new file mode 120000
--- /dev/null
+++ b/AGENTS.md
This was also reproduced outside the original project with a minimal temporary repository.
Expected behavior
CodeView should render the regular-file-to-symlink transition as one type-change item, or safely two patch blocks with distinct internal IDs. A valid Git diff should not crash the thread view.
Actual behavior
The CodeView renderer throws a duplicate-ID error and T3 Code displays a full-page "Something went wrong" state:
Error: CodeView.addItem: duplicate id "AGENTS.mdAGENTS.md"
at appendItemsInternal (t3code://app/assets/diffFileContents-*.js)
at setItems (t3code://app/assets/diffFileContents-*.js)
The repository and symlink remain valid. In a second occurrence on 2026-09-10, Try again, Reload app, and fully restarting the application did not recover the affected thread: its persisted open diff panel was restored and triggered the crash again. The original conversation was recovered by closing only its persisted right panel, as described below.
The path-only identity collision has since been confirmed by a maintainer in this comment. The actual key contains a NUL separator: AGENTS.md\0AGENTS.md, which is invisible in the displayed error.
Related but distinct:
Impact
Major degradation or frequent failure
The affected turn cannot be reviewed in CodeView. If its diff panel was open, the entire existing thread can remain inaccessible across application restarts. Creating a new thread is not necessary to recover the original conversation.
Version or commit
T3 Code desktop 0.0.40
Environment
- macOS 26.6.1, Apple Silicon arm64
- T3 Code desktop 0.0.40
- Codex CLI 0.154.0
- Node.js 26.8.1
Logs or stack traces
T3 Code (Alpha) 0.0.40
Error: CodeView.addItem: duplicate id "AGENTS.mdAGENTS.md"
at appendItemsInternal (t3code://app/assets/diffFileContents-*.js)
at setItems (t3code://app/assets/diffFileContents-*.js)
Verified recovery of the original thread (desktop 0.0.40)
A second occurrence happened after a turn completed using Claude Opus 5, so the observed failure is not limited to the Codex provider. This is an additional observation, not proof of provider causality.
Inspection of the installed 0.0.40 source maps and the live application showed:
rightPanelStore.ts persists panel state in localStorage under t3code:right-panel-state:v2 (persisted schema version 11).
- The affected entry in
state.byThreadKey had isOpen: true, surfaces: [{ id: "diff", kind: "diff" }], and activeSurfaceId: "diff".
- Changing only that entry's
isOpen to false, then reloading, restored the same thread. The conversation, latest completed response, and message composer were visibly accessible again.
- No conversation database, checkpoint, repository file, or symlink was modified for this recovery.
For another user stuck on the error page in 0.0.40, open View > Toggle Developer Tools > Console. This version-specific workaround identifies the current thread from its URL, saves a sessionStorage backup, closes only that thread's panel, and reloads:
(() => {
const storageKey = "t3code:right-panel-state:v2";
const raw = localStorage.getItem(storageKey);
const data = JSON.parse(raw ?? "null");
const threadId = location.hash.split("?")[0].split("/").filter(Boolean).at(-1);
const panels = data?.state?.byThreadKey;
const matches = Object.keys(panels ?? {}).filter(
key => key.endsWith(":" + threadId)
);
if (!threadId || matches.length !== 1) {
throw new Error("Expected exactly one current-thread panel; nothing changed.");
}
sessionStorage.setItem("t3-recovery-right-panel-backup", raw);
panels[matches[0]].isOpen = false;
localStorage.setItem(storageKey, JSON.stringify(data));
location.reload();
})();
The recovery operation was verified on the affected thread using its explicit scoped key. The snippet above generalizes key selection to the current route. Avoid reopening the affected diff until the renderer fix is available. Clearing all localStorage or deleting the thread is unnecessary.
Additional fix and regression suggestions
The maintainer's proposed unique identity fix addresses the collision. These additional cases would cover the persistent lockout and prevent partial fixes:
- Preserve both patch halves. Do not deduplicate by path and silently discard the deletion or symlink addition. If rendering two items, assign deterministic distinct IDs and use them consistently for collapse, selection/reveal, and annotation behavior. If merging, retain the old/new modes and contents.
- Contain rendering failures within the diff panel. Keep the conversation and composer mounted, with a local error fallback offering to close the panel. The close action should persist
isOpen: false, so restarting cannot restore the same failing panel indefinitely.
- Regression coverage: regular file to symlink and the reverse; both patch halves and contents preserved; unique stable item IDs; collapse/reveal behavior after rerender; and reopening a thread with a persisted open diff panel. A deliberately failing diff renderer should leave the conversation usable and allow persistent panel closure.
The persisted-panel recovery is directly verified. The implementation and regression items above are suggestions, not a tested code patch.
Before submitting
Area
apps/web
Steps to reproduce
Create a Git repository where
AGENTS.mdis a regular tracked file andCLAUDE.mdis another regular tracked file:Open the repository in T3 Code and start a thread.
During a turn, replace the regular file with a relative symlink and commit it:
rm AGENTS.md ln -s CLAUDE.md AGENTS.md git add AGENTS.md git commit -m 'use shared instructions'After the turn checkpoint is created, open the turn diff or Changes view.
The checkpoint transition is a valid Git type change:
With
git diff --no-renames, Git represents it as two file blocks with the same path:This was also reproduced outside the original project with a minimal temporary repository.
Expected behavior
CodeView should render the regular-file-to-symlink transition as one type-change item, or safely two patch blocks with distinct internal IDs. A valid Git diff should not crash the thread view.
Actual behavior
The CodeView renderer throws a duplicate-ID error and T3 Code displays a full-page "Something went wrong" state:
The repository and symlink remain valid. In a second occurrence on 2026-09-10, Try again, Reload app, and fully restarting the application did not recover the affected thread: its persisted open diff panel was restored and triggered the crash again. The original conversation was recovered by closing only its persisted right panel, as described below.
The path-only identity collision has since been confirmed by a maintainer in this comment. The actual key contains a NUL separator:
AGENTS.md\0AGENTS.md, which is invisible in the displayed error.Related but distinct:
Impact
Major degradation or frequent failure
The affected turn cannot be reviewed in CodeView. If its diff panel was open, the entire existing thread can remain inaccessible across application restarts. Creating a new thread is not necessary to recover the original conversation.
Version or commit
T3 Code desktop 0.0.40
Environment
Logs or stack traces
Verified recovery of the original thread (desktop 0.0.40)
A second occurrence happened after a turn completed using Claude Opus 5, so the observed failure is not limited to the Codex provider. This is an additional observation, not proof of provider causality.
Inspection of the installed 0.0.40 source maps and the live application showed:
rightPanelStore.tspersists panel state in localStorage undert3code:right-panel-state:v2(persisted schema version 11).state.byThreadKeyhadisOpen: true,surfaces: [{ id: "diff", kind: "diff" }], andactiveSurfaceId: "diff".isOpentofalse, then reloading, restored the same thread. The conversation, latest completed response, and message composer were visibly accessible again.For another user stuck on the error page in 0.0.40, open View > Toggle Developer Tools > Console. This version-specific workaround identifies the current thread from its URL, saves a sessionStorage backup, closes only that thread's panel, and reloads:
The recovery operation was verified on the affected thread using its explicit scoped key. The snippet above generalizes key selection to the current route. Avoid reopening the affected diff until the renderer fix is available. Clearing all localStorage or deleting the thread is unnecessary.
Additional fix and regression suggestions
The maintainer's proposed unique identity fix addresses the collision. These additional cases would cover the persistent lockout and prevent partial fixes:
isOpen: false, so restarting cannot restore the same failing panel indefinitely.The persisted-panel recovery is directly verified. The implementation and regression items above are suggestions, not a tested code patch.