Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ reviews:
- name: "cmux Swift logging"
mode: error
instructions: |
For production Swift changes, fail when the diff violates `.github/review-bot-rules/swift-logging.md`: `print`, `debugPrint`, `dump`, or `NSLog` in app/runtime code; ad hoc file/stdout logging for diagnostics; MainActor-coupled file-scoped Logger constants; or logs that expose secrets or personal data. Pass for CLI output, tests, debug-only logs, and explicitly sanitized provider diagnostics.
For production Swift changes, fail when the diff violates `.github/review-bot-rules/swift-logging.md`: `print`, `debugPrint`, `dump`, or `NSLog` in app/runtime code; ad hoc file/stdout logging for diagnostics; MainActor-coupled file-scoped Logger constants; or logs that expose secrets or personal data. Do not require new logs for new code paths; only check logging that the diff adds or materially changes. Pass for CLI output, tests, debug-only logs, and explicitly sanitized provider diagnostics.
- name: "cmux SwiftUI state layout"
mode: error
instructions: |
Expand Down
4 changes: 4 additions & 0 deletions .github/review-bot-rules/swift-logging.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Swift Unified Logging

Flag production Swift logging that bypasses Apple's unified logging system.
Do not require new logs for new code paths. This rule only checks logging that
the diff adds or materially changes. Temporary debug probes belong in the
dogfood debug loop and should be removed before merge unless the PR explicitly
states that a sparse permanent diagnostic is intended.

Report a failure when the diff introduces or materially expands:

Expand Down
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,13 @@ the source of truth for VM lifecycle, active VM limits, idempotency, and usage e

## Debug event log

All debug events (keys, mouse, focus, splits, tabs) go to a unified log in DEBUG builds:
When adding debug event instrumentation, put events (keys, mouse, focus, splits, tabs)
in the unified DEBUG build log:

This section describes the required destination and shape for debug logs when they
are added. It is not a blanket requirement to add debug logs to every new code path.
Most temporary probes should be added only during the dogfood debug loop and removed
before merge.

```bash
tail -f "$(cat /tmp/cmux-last-debug-log-path 2>/dev/null || echo /tmp/cmux-debug.log)"
Expand Down
20 changes: 20 additions & 0 deletions GhosttyTabs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
C0DE35010000000000000001 /* SidebarScrim.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE35010000000000000002 /* SidebarScrim.swift */; };
62270F3DCECB4787D789CCE3 /* SidebarWorkspaceSnapshotRefreshPolicyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F016B5C09357B3226FA2E014 /* SidebarWorkspaceSnapshotRefreshPolicyTests.swift */; };
C3408A000000000000000001 /* ContentView+RightSidebarCommandPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3408A000000000000000002 /* ContentView+RightSidebarCommandPalette.swift */; };
C3408A000000000000000005 /* ContentView+ViewCommandPalette.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3408A000000000000000006 /* ContentView+ViewCommandPalette.swift */; };
D7AB00000000000000000003 /* ContentView+MoveTabToNewWorkspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AB00000000000000000004 /* ContentView+MoveTabToNewWorkspace.swift */; };
C0DE32470000000000000001 /* ContentViewIdentifierCopyCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE32470000000000000002 /* ContentViewIdentifierCopyCommands.swift */; };
C3408A000000000000000003 /* RightSidebarCommandPaletteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3408A000000000000000004 /* RightSidebarCommandPaletteTests.swift */; };
Expand Down Expand Up @@ -194,7 +195,11 @@
A5001534 /* BrowserWindowPortal.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001533 /* BrowserWindowPortal.swift */; };
A5001540 /* PortScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001541 /* PortScanner.swift */; };
C7A501000000000000000002 /* CmuxTopSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A501000000000000000001 /* CmuxTopSnapshot.swift */; };
C7A508000000000000000002 /* CmuxTopSnapshotScopeCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A508000000000000000001 /* CmuxTopSnapshotScopeCache.swift */; };
C7A502000000000000000002 /* TaskManagerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A502000000000000000001 /* TaskManagerWindowController.swift */; };
C7A506000000000000000002 /* TaskManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A506000000000000000001 /* TaskManagerView.swift */; };
C7A507000000000000000002 /* TaskManagerResourcesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A507000000000000000001 /* TaskManagerResourcesTests.swift */; };
C7A509000000000000000002 /* CmuxTopSnapshotScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A509000000000000000001 /* CmuxTopSnapshotScopeTests.swift */; };
C7A503000000000000000002 /* TaskManagerSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A503000000000000000001 /* TaskManagerSnapshot.swift */; };
C7A504000000000000000002 /* TaskManagerTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A504000000000000000001 /* TaskManagerTypes.swift */; };
C7A505000000000000000002 /* TerminalControllerTopSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A505000000000000000001 /* TerminalControllerTopSupport.swift */; };
Expand Down Expand Up @@ -447,6 +452,7 @@
A5001012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
C0DE35010000000000000002 /* SidebarScrim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarScrim.swift; sourceTree = "<group>"; };
C3408A000000000000000002 /* ContentView+RightSidebarCommandPalette.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContentView+RightSidebarCommandPalette.swift"; sourceTree = "<group>"; };
C3408A000000000000000006 /* ContentView+ViewCommandPalette.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContentView+ViewCommandPalette.swift"; sourceTree = "<group>"; };
D7AB00000000000000000004 /* ContentView+MoveTabToNewWorkspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContentView+MoveTabToNewWorkspace.swift"; sourceTree = "<group>"; };
D7AB34300000000000000002 /* SidebarDropPlanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar/SidebarDropPlanner.swift; sourceTree = "<group>"; };
D7AB34300000000000000004 /* SidebarBonsplitTabWorkspaceDropOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar/SidebarBonsplitTabWorkspaceDropOverlay.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -487,7 +493,11 @@
A5001019 /* TerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalController.swift; sourceTree = "<group>"; };
D7AB0000000000000000000C /* TerminalController+MoveTabToNewWorkspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TerminalController+MoveTabToNewWorkspace.swift"; sourceTree = "<group>"; };
C7A501000000000000000001 /* CmuxTopSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxTopSnapshot.swift; sourceTree = "<group>"; };
C7A508000000000000000001 /* CmuxTopSnapshotScopeCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxTopSnapshotScopeCache.swift; sourceTree = "<group>"; };
C7A502000000000000000001 /* TaskManagerWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagerWindowController.swift; sourceTree = "<group>"; };
C7A506000000000000000001 /* TaskManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagerView.swift; sourceTree = "<group>"; };
C7A507000000000000000001 /* TaskManagerResourcesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagerResourcesTests.swift; sourceTree = "<group>"; };
C7A509000000000000000001 /* CmuxTopSnapshotScopeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CmuxTopSnapshotScopeTests.swift; sourceTree = "<group>"; };
C7A503000000000000000001 /* TaskManagerSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagerSnapshot.swift; sourceTree = "<group>"; };
C7A504000000000000000001 /* TaskManagerTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagerTypes.swift; sourceTree = "<group>"; };
C7A505000000000000000001 /* TerminalControllerTopSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalControllerTopSupport.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -844,6 +854,7 @@
A5001012 /* ContentView.swift */,
C0DE35010000000000000002 /* SidebarScrim.swift */,
C3408A000000000000000002 /* ContentView+RightSidebarCommandPalette.swift */,
C3408A000000000000000006 /* ContentView+ViewCommandPalette.swift */,
D7AB00000000000000000004 /* ContentView+MoveTabToNewWorkspace.swift */,
D7AB34300000000000000002 /* SidebarDropPlanner.swift */,
D7AB34300000000000000004 /* SidebarBonsplitTabWorkspaceDropOverlay.swift */,
Expand Down Expand Up @@ -908,7 +919,9 @@
D7AB0000000000000000000C /* TerminalController+MoveTabToNewWorkspace.swift */,
C7A505000000000000000001 /* TerminalControllerTopSupport.swift */,
C7A501000000000000000001 /* CmuxTopSnapshot.swift */,
C7A508000000000000000001 /* CmuxTopSnapshotScopeCache.swift */,
C7A502000000000000000001 /* TaskManagerWindowController.swift */,
C7A506000000000000000001 /* TaskManagerView.swift */,
C7A503000000000000000001 /* TaskManagerSnapshot.swift */,
C7A504000000000000000001 /* TaskManagerTypes.swift */,
A5001541 /* PortScanner.swift */,
Expand Down Expand Up @@ -1087,6 +1100,8 @@
1D301919B10F22B8708E8883 /* WorkspaceManualUnreadTests.swift */,
EE0171AF1F49F7547191CEE5 /* SidebarWidthPolicyTests.swift */,
51D800000000000000000002 /* SidebarIdentifierFormattingTests.swift */,
C7A507000000000000000001 /* TaskManagerResourcesTests.swift */,
C7A509000000000000000001 /* CmuxTopSnapshotScopeTests.swift */,
D7AB34300000000000000006 /* SidebarWorkspaceDropPlannerTests.swift */,
491751CE2321474474F27DCF /* TerminalControllerSocketSecurityTests.swift */,
9C1BEA3D2E6F49709A71C021 /* TerminalControllerSocketWriteTests.swift */,
Expand Down Expand Up @@ -1352,6 +1367,7 @@
A5001002 /* ContentView.swift in Sources */,
C0DE35010000000000000001 /* SidebarScrim.swift in Sources */,
C3408A000000000000000001 /* ContentView+RightSidebarCommandPalette.swift in Sources */,
C3408A000000000000000005 /* ContentView+ViewCommandPalette.swift in Sources */,
D7AB00000000000000000003 /* ContentView+MoveTabToNewWorkspace.swift in Sources */,
D7AB34300000000000000001 /* SidebarDropPlanner.swift in Sources */,
D7AB34300000000000000003 /* SidebarBonsplitTabWorkspaceDropOverlay.swift in Sources */,
Expand Down Expand Up @@ -1416,7 +1432,9 @@
D7AB0000000000000000000B /* TerminalController+MoveTabToNewWorkspace.swift in Sources */,
C7A505000000000000000002 /* TerminalControllerTopSupport.swift in Sources */,
C7A501000000000000000002 /* CmuxTopSnapshot.swift in Sources */,
C7A508000000000000000002 /* CmuxTopSnapshotScopeCache.swift in Sources */,
C7A502000000000000000002 /* TaskManagerWindowController.swift in Sources */,
C7A506000000000000000002 /* TaskManagerView.swift in Sources */,
C7A503000000000000000002 /* TaskManagerSnapshot.swift in Sources */,
C7A504000000000000000002 /* TaskManagerTypes.swift in Sources */,
A5001540 /* PortScanner.swift in Sources */,
Expand Down Expand Up @@ -1632,6 +1650,8 @@
0F2C25F9170130F8DC09DD1B /* WorkspaceManualUnreadTests.swift in Sources */,
CA39C0304FE351A21C372429 /* SidebarWidthPolicyTests.swift in Sources */,
51D800000000000000000001 /* SidebarIdentifierFormattingTests.swift in Sources */,
C7A507000000000000000002 /* TaskManagerResourcesTests.swift in Sources */,
C7A509000000000000000002 /* CmuxTopSnapshotScopeTests.swift in Sources */,
D7AB34300000000000000005 /* SidebarWorkspaceDropPlannerTests.swift in Sources */,
8C4BBF2DEF6DF93F395A9EE7 /* TerminalControllerSocketSecurityTests.swift in Sources */,
9C1BEA3D2E6F49709A71C020 /* TerminalControllerSocketWriteTests.swift in Sources */,
Expand Down
84 changes: 84 additions & 0 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -110565,6 +110565,90 @@
"ja": { "stringUnit": { "state": "translated", "value": "ワークスペース、ターミナル、またはブラウザサーフェスを開くとここに表示されます。" } }
}
},
"taskManager.contextMenu.viewWorkspace": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "View Workspace" } },
"ja": { "stringUnit": { "state": "translated", "value": "ワークスペースを表示" } }
}
},
"taskManager.contextMenu.viewTerminal": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "View Terminal" } },
"ja": { "stringUnit": { "state": "translated", "value": "ターミナルを表示" } }
}
},
"taskManager.contextMenu.killProcess": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Kill Process..." } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセスを終了..." } }
}
},
"taskManager.killProcess.title.one": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Kill process?" } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセスを終了しますか?" } }
}
},
"taskManager.killProcess.message.one": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Ask %@ (PID %lld) to terminate gracefully. cmux will force-kill it if it is still running after a short grace period." } },
"ja": { "stringUnit": { "state": "translated", "value": "%@(PID %lld)に正常終了を要求します。短い猶予時間の後も実行中の場合、cmux が強制終了します。" } }
}
},
"taskManager.killProcess.title.other": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Kill processes?" } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセスを終了しますか?" } }
}
},
"taskManager.killProcess.message.other": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Ask %lld processes to terminate gracefully. cmux will force-kill remaining processes after a short grace period. PIDs: %@." } },
"ja": { "stringUnit": { "state": "translated", "value": "%lld 個のプロセスに正常終了を要求します。短い猶予時間の後も残っているプロセスは cmux が強制終了します。PID: %@。" } }
}
},
"taskManager.killProcess.confirm": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Kill" } },
"ja": { "stringUnit": { "state": "translated", "value": "終了" } }
}
},
"taskManager.killProcess.cancel": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Cancel" } },
"ja": { "stringUnit": { "state": "translated", "value": "キャンセル" } }
}
},
"taskManager.killProcess.error": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Unable to kill process: %@" } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセスを終了できません: %@" } }
}
},
Comment on lines +110631 to +110637

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 The taskManager.killProcess.error xcstrings entries use two C-format specifiers (%lld for the PID and %@ for the error message), but the Swift call site builds a single pre-joined detail string and passes it as one String.LocalizationValue interpolation argument. Swift's String(localized:defaultValue:) substitutes positional arguments from the defaultValue interpolation list into the resolved xcstrings template by position. With only one argument supplied and two specifiers in the template, the first specifier (%lld) receives a String where an Int64 is expected, and the second (%@) gets nothing — producing a malformed or literally un-substituted error string in both en and ja locales. The xcstrings entry should use a single %@ to match the one pre-formatted String argument the Swift code actually passes.

Suggested change
"taskManager.killProcess.error": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Unable to kill process %lld: %@" } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセス %lld を終了できません: %@" } }
}
},
"taskManager.killProcess.error": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Unable to kill process: %@" } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセスを終了できません: %@" } }
}
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The error localization now has one %@ placeholder and the call sites format it with the prebuilt error detail string.

— Claude Code

"taskManager.killProcess.target.processGroup": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "process group %lld" } },
"ja": { "stringUnit": { "state": "translated", "value": "プロセスグループ %lld" } }
}
},
"taskManager.killProcess.target.pid": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "PID %lld" } },
"ja": { "stringUnit": { "state": "translated", "value": "PID %lld" } }
}
},
"taskManager.updated.never": {
"extractionState": "manual",
"localizations": {
Expand Down
Loading