Remove Swift file length budget (#8125)

Co-authored-by: cmux reload-cloud <[email protected]>
This commit is contained in:
Abdulaziz Albahar
2026-07-14 22:28:38 -05:00
committed by GitHub
co-authored by cmux reload-cloud
parent 671aff6812
commit 85ded20f53
13 changed files with 37 additions and 1396 deletions
+3 -3
View File
@@ -36,7 +36,7 @@ reviews:
Apply `.github/review-bot-rules/react-base-ui-accessibility.md` during review. For custom React UI, require `@base-ui-components/react` or an existing local component when it provides the relevant dialog, popover, menu, checkbox, select, switch, tabs, tooltip, combobox, focus, or keyboard behavior. Pass for native semantic controls and for cases with no relevant primitive where the PR owns complete accessibility and keyboard behavior.
- path: "**/Package.swift"
instructions: |
Apply the cmux custom Swift lint rules in `.github/review-bot-rules/`, especially the concurrency modernization, actor isolation, blocking runtime, file/package boundary, and architectural rethink rules.
Apply the cmux custom Swift lint rules in `.github/review-bot-rules/`, especially the concurrency modernization, actor isolation, blocking runtime, package boundary, and architectural rethink rules.
- path: "**/Package.resolved"
instructions: |
Apply `.github/review-bot-rules/swiftpm-package-resolved.md` during review. cmux-owned SwiftPM lockfiles are intentional source-of-truth files, not accidental artifacts; dependency pin changes must be visible in PR diffs.
@@ -142,10 +142,10 @@ reviews:
mode: error
instructions: |
For Swift changes, fail when the diff violates `.github/review-bot-rules/swift-concurrent-annotation.md`: missing `@concurrent` on `nonisolated async` work that should leave the caller actor, invalid `@concurrent` on synchronous or actor-isolated functions, or CPU/file/network-heavy async helpers called from UI isolation without an explicit hop. Pass for intentionally UI-bound async work.
- name: "cmux Swift file and package boundaries"
- name: "cmux Swift package boundaries"
mode: error
instructions: |
For production Swift changes, fail when the diff violates `.github/review-bot-rules/swift-file-package-boundaries.md`: new oversized files, large additions to already oversized files, mixed UI/state/persistence/network/parsing/protocol responsibilities in one file, or independently testable feature logic kept in the app target when it should live behind a small SwiftPM package target. Pass for existing oversized files touched incidentally, small UI/AppKit/Ghostty glue, generated/vendored/prototype/test code, and focused bug fixes that preserve a clear extraction path.
For production Swift changes, fail when the diff violates `.github/review-bot-rules/swift-package-boundaries.md`: independently testable or reusable domain logic is kept in the app target when it should live behind a small SwiftPM package target. Pass for small UI/AppKit/Ghostty glue, generated/vendored/prototype/test code, and app-lifecycle composition.
- name: "cmux SwiftPM lockfiles"
mode: error
instructions: |
+1 -1
View File
@@ -27,7 +27,7 @@ Current rules:
- `swift-concurrency-modernization.md`
- `swift-concurrent-annotation.md`
- `swift-expensive-sync-load.md`
- `swift-file-package-boundaries.md`
- `swift-package-boundaries.md`
- `swift-logging.md`
- `swiftui-state-layout.md`
- `user-facing-errors.md`
@@ -1,34 +0,0 @@
# Swift File And Package Boundaries
Flag Swift changes that add too much unrelated responsibility to one file or keep independently testable feature logic inside the app target when it should be isolated behind a SwiftPM package boundary.
cmux already has a checked-in Swift file length budget reference (`.github/swift-file-length-budget.tsv` and `scripts/swift_file_length_budget.py`). The CI gate is diff-aware: small incidental growth in an existing over-budget file is allowed, while new large files, meaningful PR-local growth, and hard-cap violations still fail. This rule is the semantic review layer: do not satisfy it by mechanically moving code around, and do not expand the TSV budget for a feature that should instead be split by responsibility or extracted into a package.
Report a failure when the diff introduces or materially expands:
- A new production Swift file over 400 lines without a clear single responsibility, or over 800 lines even when the responsibility is mostly coherent.
- More than 250 lines added to an existing production Swift file that is already over 800 lines. Treat an extraction exception as met only when the PR removes or moves one of the mixed responsibilities listed below out of the file, or documents that responsibility behind a new package boundary, and the file's total line count decreases by more than 200 lines. A new SwiftPM package target can satisfy this exception when the oversized file also shrinks by more than 200 lines because code moved into that target.
- A file that mixes UI rendering, state ownership, persistence, networking, parsing, subprocess/socket protocol, and platform bridge code in one place.
- A feature implemented directly in the app target/module's root `Sources/` path when its core logic is independent of cmux app lifecycle and can compile/test without AppKit, SwiftUI view state, Ghostty globals, or process-wide singletons.
- Reusable domain logic used by more than one surface (Mac app, CLI, daemon, tests, previews, debug tooling, future iOS/shared code) without a small SwiftPM package target.
- Provider, auth, protocol, parsing, persistence, logging, or workstream logic that needs isolated fakes, fixtures, or unit tests but is hidden behind app-target globals.
- A PR that primarily updates `.github/swift-file-length-budget.tsv` to accept meaningful growth instead of reducing the large file, splitting responsibilities, or adding a package boundary.
Line counting follows the existing budget script as a shared measurement convention: count physical lines including blank lines; scan cmux-owned Swift files under `Sources`, `CLI`, `Packages`, `cmuxTests`, and `cmuxUITests`; exclude whole path subtrees containing `/vendor/`, `/ghostty/`, `/homebrew-cmux/`, `/.build/`, `/SourcePackages/`, or `/.ci-source-packages/`; and use 500 lines as the tracked-file reference threshold from `.github/swift-file-length-budget.tsv`. For this LLM rule, use the post-change physical file length when visible; use PR added-line count only for the "more than 250 lines added" growth check. Do not object to a small focused bug fix merely because it adds a few lines to an existing oversized file.
Package-boundary signals:
- The code has a stable domain noun and public API that can be expressed without view types.
- The code needs tests that should run without launching cmux or constructing app UI.
- The code owns data formats, network/provider contracts, socket messages, credentials, persistence schemas, or cross-surface state transitions.
- The feature would be safer if callers depended on a small protocol or value API instead of a concrete app singleton.
Allowed cases:
- Existing oversized files that the PR only touches incidentally.
- Small UI-only views, AppKit bridges, app delegates, menu wiring, and Ghostty integration glue that are inherently app-target code.
- Focused bug fixes that add a small amount of code to a large file while preserving a clear extraction path.
- Generated files, vendored code, prototypes, and test fixtures.
- New package creation that starts small and intentionally leaves app-specific UI composition in the app target/module-root `Sources/` directory.
When reporting, include the file or feature boundary, the approximate line-count pressure, the responsibilities being mixed, and the smallest extraction cut. If a package is the right shape, name the proposed package target and the first public type or protocol it should expose.
@@ -0,0 +1,24 @@
# Swift Package Boundaries
Flag Swift changes that keep independently testable feature logic inside the app target when it should be isolated behind a SwiftPM package boundary.
Report a failure when the diff introduces or materially expands:
- A feature implemented directly in the app target/module's root `Sources/` path when its core logic is independent of cmux app lifecycle and can compile/test without AppKit, SwiftUI view state, Ghostty globals, or process-wide singletons.
- Reusable domain logic used by more than one surface (Mac app, CLI, daemon, tests, previews, debug tooling, future iOS/shared code) without a small SwiftPM package target.
- Provider, auth, protocol, parsing, persistence, logging, or workstream logic that needs isolated fakes, fixtures, or unit tests but is hidden behind app-target globals.
Package-boundary signals:
- The code has a stable domain noun and public API that can be expressed without view types.
- The code needs tests that should run without launching cmux or constructing app UI.
- The code owns data formats, network/provider contracts, socket messages, credentials, persistence schemas, or cross-surface state transitions.
- The feature would be safer if callers depended on a small protocol or value API instead of a concrete app singleton.
Allowed cases:
- Small UI-only views, AppKit bridges, app delegates, menu wiring, and Ghostty integration glue that are inherently app-target code.
- Generated files, vendored code, prototypes, and test fixtures.
- New package creation that starts small and intentionally leaves app-specific UI composition in the app target/module-root `Sources/` directory.
When reporting, include the feature boundary and the smallest extraction cut. Name the proposed package target and the first public type or protocol it should expose.
-257
View File
@@ -1,257 +0,0 @@
# cmux-owned Swift file length budget.
# Format: max_lines<TAB>relative path
# Reduce counts as files shrink. CI fails if tracked files exceed this budget.
35405 CLI/cmux.swift
17782 Sources/AppDelegate.swift
15818 Sources/ContentView.swift
14701 Sources/TerminalController.swift
12829 Sources/Workspace.swift
12554 Sources/GhosttyTerminalView.swift
12262 cmuxTests/AppDelegateShortcutRoutingTests.swift
11332 Sources/Panels/BrowserPanel.swift
9497 cmuxTests/CLINotifyProcessIntegrationRegressionTests.swift
7968 CLI/cmux_open.swift
7756 Sources/Panels/BrowserPanelView.swift
7489 cmuxTests/WorkspaceUnitTests.swift
7473 Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
7310 cmuxTests/WorkspaceRemoteConnectionTests.swift
6359 cmuxTests/SessionPersistenceTests.swift
6255 cmuxTests/GhosttyConfigTests.swift
6188 Sources/TabManager.swift
5857 cmuxTests/TerminalAndGhosttyTests.swift
5782 Sources/TextBoxInput.swift
5571 cmuxTests/BrowserConfigTests.swift
4735 Sources/cmuxApp.swift
4482 Sources/Panels/FilePreviewPanel.swift
4196 cmuxTests/BrowserPanelTests.swift
4004 cmuxTests/TabManagerUnitTests.swift
3965 Sources/BrowserWindowPortal.swift
3953 cmuxTests/WindowAndDragTests.swift
3934 Sources/Feed/FeedPanelView.swift
3779 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
3673 cmuxTests/TabManagerSessionSnapshotTests.swift
3668 cmuxTests/CLIGenericHookPersistenceTests.swift
3314 Sources/CmuxConfig.swift
2882 Sources/Update/UpdateTitlebarAccessory.swift
2875 Sources/SessionIndexView.swift
2874 cmuxTests/CMUXOpenCommandTests.swift
2561 Sources/Panels/CmuxWebView.swift
2558 Sources/KeyboardShortcutSettings.swift
2546 cmuxTests/WorkspaceManualUnreadTests.swift
2524 cmuxTests/CommandPaletteSearchEngineTests.swift
2328 cmuxTests/CJKIMEInputTests.swift
2257 Sources/Mobile/MobileHostService.swift
2229 Sources/TerminalWindowPortal.swift
2216 Sources/TerminalNotificationStore.swift
2186 Sources/RestorableAgentSession.swift
2133 cmuxTests/ShortcutAndCommandPaletteTests.swift
2126 cmuxTests/CmuxConfigTests.swift
1999 Sources/SessionPersistence.swift
1983 Sources/KeyboardShortcutSettingsFileStore.swift
1900 cmuxTests/NotificationAndMenuBarTests.swift
1866 Sources/Panels/BrowserWebAuthnSupport.swift
1847 cmuxTests/TerminalControllerSocketSecurityTests.swift
1810 Sources/SessionIndexStore.swift
1760 Sources/WindowDragHandleView.swift
1732 cmuxTests/WorkspacePullRequestSidebarTests.swift
1687 cmuxTests/MarkdownPanelTests.swift
1680 cmuxUITests/BrowserPaneNavigationKeybindUITests.swift
1656 Sources/FileExplorerView.swift
1652 cmuxTests/CMUXCLIErrorOutputRegressionTests.swift
1597 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/TerminalInputTextView.swift
1560 cmuxTests/TextBoxMentionCompletionTests.swift
1523 cmuxTests/RestorableAgentSessionIndexTests.swift
1499 cmuxTests/OmnibarAndToolsTests.swift
1498 cmuxUITests/MultiWindowNotificationsUITests.swift
1428 cmuxTests/AgentSessionAutoResumeSwiftTests.swift
1420 cmuxTests/AppDelegateIssue2907RoutingTests.swift
1384 cmuxTests/KeyboardShortcutSettingsFileStoreStartupTests.swift
1380 cmuxUITests/MenuKeyEquivalentRoutingUITests.swift
1363 Sources/CMUXInstalledExtensionSidebarHostView.swift
1360 Sources/Feed/FeedButtonStyleDebugWindowController.swift
1317 Sources/FileExplorerStore.swift
1290 Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Config/GhosttyConfig.swift
1290 cmuxTests/TextBoxSubmitActionTests.swift
1285 cmuxUITests/SidebarHelpMenuUITests.swift
1270 cmuxTests/MobileHostAuthorizationTests.swift
1258 Sources/Feed/FeedCoordinator.swift
1240 cmuxTests/SidebarOrderingTests.swift
1209 Packages/macOS/CmuxCommandPalette/Tests/CmuxCommandPaletteTests/CommandPaletteSearchEngineTests.swift
1204 cmuxTests/FileExplorerStoreTests.swift
1197 cmuxTests/CodexAppServerSessionTests.swift
1197 cmuxTests/VMDefaultCloudCommandTests.swift
1147 cmuxTests/PiVaultAgentPersistenceTests.swift
1117 cmuxTests/AgentHibernationTests.swift
1093 cmuxUITests/BonsplitTabDragUITests.swift
1087 Packages/macOS/CmuxCommandPalette/Sources/CmuxCommandPalette/Search/CommandPaletteFuzzyMatcher.swift
1049 cmuxTests/WorkspaceGroupTests.swift
1038 Sources/AppDelegate+CmuxSSHURL.swift
1030 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/TerminalViewportResyncTests.swift
1021 cmuxUITests/TerminalCmdClickUITests.swift
1009 cmuxTests/CmuxTopSnapshotScopeTests.swift
1006 cmuxTests/CmuxSSHURLRequestTests.swift
1000 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/PairedMacBackupTests.swift
999 cmuxTests/DockSocketLifecycleTests.swift
982 Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
951 Sources/App/TerminalDirectoryOpenSupport.swift
948 Sources/App/ShortcutRoutingSupport.swift
947 Sources/TerminalNotificationPolicy.swift
945 Sources/SessionIndexRegisteredAgents.swift
944 Sources/CommandPalette/CommandPaletteSettingsToggle.swift
943 Sources/Cloud/VMClient.swift
942 Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceMacSelectionTests.swift
929 Sources/Panels/TerminalPanel.swift
918 Sources/Panels/BrowserPopupWindowController.swift
905 Sources/CmuxSSHURLRequest.swift
904 Sources/VaultAgentProcessScanner.swift
899 Sources/Panels/MarkdownWebRenderer.swift
896 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
895 Sources/RemoteTmuxControlConnection.swift
885 cmuxTests/SidebarWorkspaceDropPlannerTests.swift
878 Sources/PortScanner.swift
871 cmuxTests/ClaudeHookSurfaceResolutionSwiftTests.swift
868 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift
868 Sources/Panels/BrowserScreenshotSnapshotter.swift
865 Sources/DockSplitStore.swift
864 Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
858 Sources/PricingPlansScreen.swift
856 Sources/TextBoxMentionIndexStore.swift
847 cmuxTests/AgentSessionAutoResumeSettingsTests.swift
844 cmuxTests/SSHStartupSignalLifecycleTests.swift
837 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/TerminalOutputDeliveryQueueTests.swift
834 Sources/MainWindowFocusController.swift
825 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift
822 Sources/WorkspaceContentView.swift
812 Sources/TerminalController+ControlPaneContext.swift
810 Packages/macOS/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/SwiftViewInterpreterTests.swift
801 Sources/ClosedItemHistory.swift
798 cmuxTests/CmuxEventBusTests.swift
795 Sources/RemoteTmuxController.swift
786 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+Input.swift
779 cmuxUITests/BrowserOmnibarSuggestionsUITests.swift
773 Sources/App/MenuBarExtraController.swift
768 cmuxUITests/BrowserFixtureInteractionUITests.swift
766 Sources/Mobile/AgentChat/AgentChatSessionRegistry.swift
756 Sources/Panels/AgentSessionWebRendererCoordinator.swift
754 cmuxTests/GhosttyTerminalStartupEnvironmentTests.swift
752 cmuxUITests/CloseWorkspaceCmdDUITests.swift
747 Packages/Shared/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
738 Packages/macOS/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
736 Sources/TerminalController+ControlWorkspaceContext.swift
722 Sources/TaskManagerTypes.swift
716 Sources/TaskManagerSnapshot.swift
714 Sources/AppleScriptSupport.swift
713 cmuxTests/UpdatePillReleaseVisibilityTests.swift
710 Sources/TerminalSSHSessionDetector.swift
709 Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/SidebarDrop/SidebarWorkspaceReorderDropResolver.swift
706 CLI/CMUXCLI+Config.swift
699 cmuxTests/TerminalNotificationClearAllTests.swift
698 cmuxTests/RestorableAgentHookProviderResumeTests.swift
696 cmuxTests/KeyboardShortcutContextTests.swift
691 Sources/NotificationSoundSettings.swift
691 cmuxTests/TaskManagerResourcesTests.swift
690 cmuxTests/SessionIndexViewTests.swift
683 Packages/macOS/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swift
683 Sources/Panels/CodexAppServerSession.swift
681 Sources/Panels/AgentSessionProcessStore.swift
680 Sources/FileExplorerSearchController.swift
677 Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+Bootstrap.swift
672 cmuxTests/SessionPersistenceResumeBindingTests.swift
668 cmuxTests/FeedCoordinatorTests.swift
665 cmuxTests/CLICodexHookTimeoutRegressionTests.swift
663 Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift
660 Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
658 Packages/iOS/CmuxMobileTransport/Sources/CmuxMobileTransport/CmxNetworkByteTransport.swift
657 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridInputCatchUpTests.swift
657 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeLifecycle.swift
654 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift
654 Sources/CmuxTopSnapshot.swift
652 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTestSupport.swift
650 Packages/macOS/CmuxBrowser/Sources/CmuxBrowser/Import/Detection/BrowserInstalledBrowserDetector.swift
650 Sources/Panels/MarkdownRemoteImageLoader.swift
649 Packages/macOS/CmuxRemoteWorkspace/Sources/CmuxRemoteWorkspace/Tunnel/RemoteDaemonProxyTunnel.swift
648 cmuxTests/TerminalNotificationQueueTests.swift
644 Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentLaunchSanitizerTests.swift
641 cmuxTests/CommandPaletteNucleoFFITests.swift
637 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
635 Sources/Panels/BrowserNavigationDelegate.swift
635 cmuxUITests/RightSidebarChromeHeightUITests.swift
633 Sources/SettingsNavigation.swift
630 Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutWhenClause.swift
627 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
623 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Surface/ControlCommandCoordinator+Surface.swift
620 cmuxTests/FinderFileDropRegressionTests.swift
619 Sources/CmuxEventBus.swift
608 Sources/SleepyFaceView.swift
608 Sources/TextBoxSubmitActions.swift
608 cmuxUITests/FeedSidebarUITests.swift
607 Sources/SessionIndexModels.swift
604 Packages/macOS/CmuxCommandPalette/Tests/CmuxCommandPaletteTests/CommandPaletteNucleoFFITests.swift
601 Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Coordinators/WorkspaceReorderCoordinator.swift
594 cmuxTests/PortalTabDragRoutingTests.swift
591 Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/DefaultsValueModelLifecycleTests.swift
591 cmuxTests/CmuxConfigContextMenuTests.swift
588 cmuxTests/CommandPaletteShortcutCustomizationTests.swift
586 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttyRuntime.swift
586 Sources/JSONCParser.swift
585 cmuxTests/SettingsWindowPresenterTests.swift
583 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTests.swift
581 Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerPrimaryPolicies.swift
580 Packages/macOS/CmuxExtensionKit/Tests/CmuxExtensionKitTests/CmuxExtensionKitTests.swift
580 cmuxTests/CLIHookNoResponseTests.swift
579 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Pane/ControlCommandCoordinator+Pane.swift
578 Packages/macOS/CmuxWorkspaces/Tests/CmuxWorkspacesTests/WorkspaceCoordinatorTests.swift
577 cmuxTests/AppearanceSettingsTests.swift
576 Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileTerminalRenderGrid.swift
575 Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Coordinators/WorkspaceGroupCoordinator.swift
572 Sources/Feed/FeedTextEditorDebugWindowController.swift
567 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift
567 Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/ConfigDiscovery/GhosttyConfigDiscovery.swift
562 Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/BackingUpPairedMacStore.swift
562 cmuxTests/AgentExecutableResolverTests.swift
561 cmuxTests/GhosttyConfigPathResolverTests.swift
560 cmuxTests/CLISSHPTYResizeInputTests.swift
560 cmuxTests/RemoteTmuxControlParserTests.swift
559 CLI/CMUXCLI+AgentHookDefinitions.swift
558 Packages/macOS/CmuxGit/Sources/CmuxGit/Parsing/GitMetadataService+Config.swift
553 Sources/RightSidebarPanelView.swift
551 cmuxUITests/AutomationSocketUITests.swift
550 Sources/CloudVMActionLauncher.swift
549 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Sidebar/ControlCommandCoordinator+SidebarMetadataV1.swift
549 Sources/Panels/BrowserAutomation.swift
546 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AutomationSection.swift
544 cmuxUITests/DisplayResolutionRegressionUITests.swift
540 Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileTerminalRenderGridReplay.swift
539 CLI/CMUXCLI+Themes.swift
539 CLI/CodexTeamsApprovalBridge.swift
538 Packages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/MobileTerminalRenderGridTests.swift
538 Sources/TerminalController+ControlSurfaceContext2.swift
537 Sources/App/WorkspaceRuntimeSettings.swift
527 CLI/CLISocketPathResolver.swift
527 cmuxTests/BrowserHTTPBasicAuthPromptTests.swift
526 Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamStore.swift
525 Packages/macOS/CmuxSettings/Sources/CmuxSettings/SocketControl/SocketControlSettings.swift
524 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift
522 Sources/TerminalPaneDropTargetView.swift
520 CLI/CMUXCLI+AmpExtension.swift
520 cmuxTests/MainWindowVisibilityControllerTests.swift
519 CLI/CMUXCLI+AutoNaming.swift
519 Packages/macOS/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/Corpus/stress-two-column-cockpit-sidebar.swift
518 Packages/macOS/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/Corpus/stress-git-review-queue-command-deck.swift
516 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/CMUXMobileRootView.swift
516 Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs.swift
516 Sources/TerminalImageTransfer.swift
514 Packages/macOS/CmuxSwiftRender/Sources/CmuxSwiftRender/ExpressionEvaluator.swift
514 cmuxUITests/UpdatePillUITests.swift
511 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Sidebar/ControlCommandCoordinator+SidebarReportsV1.swift
510 Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Transcript/ChatTranscriptTableView.swift
510 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift
509 Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerAdditionalPolicies.swift
507 Sources/TerminalControllerTopSupport.swift
506 Sources/App/MainWindowVisibilityController.swift
504 Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/UserDefaultsSettingsStoreTests.swift
504 cmuxTests/TerminalNotificationSocketActionTests.swift
503 Sources/Settings/ConfigSource.swift
502 Sources/CmuxEventPublishing.swift
501 Sources/TerminalNotificationQueue.swift
500 Sources/KeyboardShortcutRecorder.swift
1 # cmux-owned Swift file length budget.
2 # Format: max_lines<TAB>relative path
3 # Reduce counts as files shrink. CI fails if tracked files exceed this budget.
4 35405 CLI/cmux.swift
5 17782 Sources/AppDelegate.swift
6 15818 Sources/ContentView.swift
7 14701 Sources/TerminalController.swift
8 12829 Sources/Workspace.swift
9 12554 Sources/GhosttyTerminalView.swift
10 12262 cmuxTests/AppDelegateShortcutRoutingTests.swift
11 11332 Sources/Panels/BrowserPanel.swift
12 9497 cmuxTests/CLINotifyProcessIntegrationRegressionTests.swift
13 7968 CLI/cmux_open.swift
14 7756 Sources/Panels/BrowserPanelView.swift
15 7489 cmuxTests/WorkspaceUnitTests.swift
16 7473 Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
17 7310 cmuxTests/WorkspaceRemoteConnectionTests.swift
18 6359 cmuxTests/SessionPersistenceTests.swift
19 6255 cmuxTests/GhosttyConfigTests.swift
20 6188 Sources/TabManager.swift
21 5857 cmuxTests/TerminalAndGhosttyTests.swift
22 5782 Sources/TextBoxInput.swift
23 5571 cmuxTests/BrowserConfigTests.swift
24 4735 Sources/cmuxApp.swift
25 4482 Sources/Panels/FilePreviewPanel.swift
26 4196 cmuxTests/BrowserPanelTests.swift
27 4004 cmuxTests/TabManagerUnitTests.swift
28 3965 Sources/BrowserWindowPortal.swift
29 3953 cmuxTests/WindowAndDragTests.swift
30 3934 Sources/Feed/FeedPanelView.swift
31 3779 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
32 3673 cmuxTests/TabManagerSessionSnapshotTests.swift
33 3668 cmuxTests/CLIGenericHookPersistenceTests.swift
34 3314 Sources/CmuxConfig.swift
35 2882 Sources/Update/UpdateTitlebarAccessory.swift
36 2875 Sources/SessionIndexView.swift
37 2874 cmuxTests/CMUXOpenCommandTests.swift
38 2561 Sources/Panels/CmuxWebView.swift
39 2558 Sources/KeyboardShortcutSettings.swift
40 2546 cmuxTests/WorkspaceManualUnreadTests.swift
41 2524 cmuxTests/CommandPaletteSearchEngineTests.swift
42 2328 cmuxTests/CJKIMEInputTests.swift
43 2257 Sources/Mobile/MobileHostService.swift
44 2229 Sources/TerminalWindowPortal.swift
45 2216 Sources/TerminalNotificationStore.swift
46 2186 Sources/RestorableAgentSession.swift
47 2133 cmuxTests/ShortcutAndCommandPaletteTests.swift
48 2126 cmuxTests/CmuxConfigTests.swift
49 1999 Sources/SessionPersistence.swift
50 1983 Sources/KeyboardShortcutSettingsFileStore.swift
51 1900 cmuxTests/NotificationAndMenuBarTests.swift
52 1866 Sources/Panels/BrowserWebAuthnSupport.swift
53 1847 cmuxTests/TerminalControllerSocketSecurityTests.swift
54 1810 Sources/SessionIndexStore.swift
55 1760 Sources/WindowDragHandleView.swift
56 1732 cmuxTests/WorkspacePullRequestSidebarTests.swift
57 1687 cmuxTests/MarkdownPanelTests.swift
58 1680 cmuxUITests/BrowserPaneNavigationKeybindUITests.swift
59 1656 Sources/FileExplorerView.swift
60 1652 cmuxTests/CMUXCLIErrorOutputRegressionTests.swift
61 1597 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/TerminalInputTextView.swift
62 1560 cmuxTests/TextBoxMentionCompletionTests.swift
63 1523 cmuxTests/RestorableAgentSessionIndexTests.swift
64 1499 cmuxTests/OmnibarAndToolsTests.swift
65 1498 cmuxUITests/MultiWindowNotificationsUITests.swift
66 1428 cmuxTests/AgentSessionAutoResumeSwiftTests.swift
67 1420 cmuxTests/AppDelegateIssue2907RoutingTests.swift
68 1384 cmuxTests/KeyboardShortcutSettingsFileStoreStartupTests.swift
69 1380 cmuxUITests/MenuKeyEquivalentRoutingUITests.swift
70 1363 Sources/CMUXInstalledExtensionSidebarHostView.swift
71 1360 Sources/Feed/FeedButtonStyleDebugWindowController.swift
72 1317 Sources/FileExplorerStore.swift
73 1290 Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/Config/GhosttyConfig.swift
74 1290 cmuxTests/TextBoxSubmitActionTests.swift
75 1285 cmuxUITests/SidebarHelpMenuUITests.swift
76 1270 cmuxTests/MobileHostAuthorizationTests.swift
77 1258 Sources/Feed/FeedCoordinator.swift
78 1240 cmuxTests/SidebarOrderingTests.swift
79 1209 Packages/macOS/CmuxCommandPalette/Tests/CmuxCommandPaletteTests/CommandPaletteSearchEngineTests.swift
80 1204 cmuxTests/FileExplorerStoreTests.swift
81 1197 cmuxTests/CodexAppServerSessionTests.swift
82 1197 cmuxTests/VMDefaultCloudCommandTests.swift
83 1147 cmuxTests/PiVaultAgentPersistenceTests.swift
84 1117 cmuxTests/AgentHibernationTests.swift
85 1093 cmuxUITests/BonsplitTabDragUITests.swift
86 1087 Packages/macOS/CmuxCommandPalette/Sources/CmuxCommandPalette/Search/CommandPaletteFuzzyMatcher.swift
87 1049 cmuxTests/WorkspaceGroupTests.swift
88 1038 Sources/AppDelegate+CmuxSSHURL.swift
89 1030 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/TerminalViewportResyncTests.swift
90 1021 cmuxUITests/TerminalCmdClickUITests.swift
91 1009 cmuxTests/CmuxTopSnapshotScopeTests.swift
92 1006 cmuxTests/CmuxSSHURLRequestTests.swift
93 1000 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/PairedMacBackupTests.swift
94 999 cmuxTests/DockSocketLifecycleTests.swift
95 982 Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
96 951 Sources/App/TerminalDirectoryOpenSupport.swift
97 948 Sources/App/ShortcutRoutingSupport.swift
98 947 Sources/TerminalNotificationPolicy.swift
99 945 Sources/SessionIndexRegisteredAgents.swift
100 944 Sources/CommandPalette/CommandPaletteSettingsToggle.swift
101 943 Sources/Cloud/VMClient.swift
102 942 Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceMacSelectionTests.swift
103 929 Sources/Panels/TerminalPanel.swift
104 918 Sources/Panels/BrowserPopupWindowController.swift
105 905 Sources/CmuxSSHURLRequest.swift
106 904 Sources/VaultAgentProcessScanner.swift
107 899 Sources/Panels/MarkdownWebRenderer.swift
108 896 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
109 895 Sources/RemoteTmuxControlConnection.swift
110 885 cmuxTests/SidebarWorkspaceDropPlannerTests.swift
111 878 Sources/PortScanner.swift
112 871 cmuxTests/ClaudeHookSurfaceResolutionSwiftTests.swift
113 868 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift
114 868 Sources/Panels/BrowserScreenshotSnapshotter.swift
115 865 Sources/DockSplitStore.swift
116 864 Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
117 858 Sources/PricingPlansScreen.swift
118 856 Sources/TextBoxMentionIndexStore.swift
119 847 cmuxTests/AgentSessionAutoResumeSettingsTests.swift
120 844 cmuxTests/SSHStartupSignalLifecycleTests.swift
121 837 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/TerminalOutputDeliveryQueueTests.swift
122 834 Sources/MainWindowFocusController.swift
123 825 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift
124 822 Sources/WorkspaceContentView.swift
125 812 Sources/TerminalController+ControlPaneContext.swift
126 810 Packages/macOS/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/SwiftViewInterpreterTests.swift
127 801 Sources/ClosedItemHistory.swift
128 798 cmuxTests/CmuxEventBusTests.swift
129 795 Sources/RemoteTmuxController.swift
130 786 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+Input.swift
131 779 cmuxUITests/BrowserOmnibarSuggestionsUITests.swift
132 773 Sources/App/MenuBarExtraController.swift
133 768 cmuxUITests/BrowserFixtureInteractionUITests.swift
134 766 Sources/Mobile/AgentChat/AgentChatSessionRegistry.swift
135 756 Sources/Panels/AgentSessionWebRendererCoordinator.swift
136 754 cmuxTests/GhosttyTerminalStartupEnvironmentTests.swift
137 752 cmuxUITests/CloseWorkspaceCmdDUITests.swift
138 747 Packages/Shared/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
139 738 Packages/macOS/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
140 736 Sources/TerminalController+ControlWorkspaceContext.swift
141 722 Sources/TaskManagerTypes.swift
142 716 Sources/TaskManagerSnapshot.swift
143 714 Sources/AppleScriptSupport.swift
144 713 cmuxTests/UpdatePillReleaseVisibilityTests.swift
145 710 Sources/TerminalSSHSessionDetector.swift
146 709 Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/SidebarDrop/SidebarWorkspaceReorderDropResolver.swift
147 706 CLI/CMUXCLI+Config.swift
148 699 cmuxTests/TerminalNotificationClearAllTests.swift
149 698 cmuxTests/RestorableAgentHookProviderResumeTests.swift
150 696 cmuxTests/KeyboardShortcutContextTests.swift
151 691 Sources/NotificationSoundSettings.swift
152 691 cmuxTests/TaskManagerResourcesTests.swift
153 690 cmuxTests/SessionIndexViewTests.swift
154 683 Packages/macOS/CmuxSwiftRender/Sources/CmuxSwiftRender/SwiftViewInterpreter.swift
155 683 Sources/Panels/CodexAppServerSession.swift
156 681 Sources/Panels/AgentSessionProcessStore.swift
157 680 Sources/FileExplorerSearchController.swift
158 677 Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+Bootstrap.swift
159 672 cmuxTests/SessionPersistenceResumeBindingTests.swift
160 668 cmuxTests/FeedCoordinatorTests.swift
161 665 cmuxTests/CLICodexHookTimeoutRegressionTests.swift
162 663 Packages/iOS/CmuxMobilePairedMac/Sources/CmuxMobilePairedMac/MobilePairedMacStore.swift
163 660 Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
164 658 Packages/iOS/CmuxMobileTransport/Sources/CmuxMobileTransport/CmxNetworkByteTransport.swift
165 657 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridInputCatchUpTests.swift
166 657 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeLifecycle.swift
167 654 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift
168 654 Sources/CmuxTopSnapshot.swift
169 652 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTestSupport.swift
170 650 Packages/macOS/CmuxBrowser/Sources/CmuxBrowser/Import/Detection/BrowserInstalledBrowserDetector.swift
171 650 Sources/Panels/MarkdownRemoteImageLoader.swift
172 649 Packages/macOS/CmuxRemoteWorkspace/Sources/CmuxRemoteWorkspace/Tunnel/RemoteDaemonProxyTunnel.swift
173 648 cmuxTests/TerminalNotificationQueueTests.swift
174 644 Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentLaunchSanitizerTests.swift
175 641 cmuxTests/CommandPaletteNucleoFFITests.swift
176 637 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
177 635 Sources/Panels/BrowserNavigationDelegate.swift
178 635 cmuxUITests/RightSidebarChromeHeightUITests.swift
179 633 Sources/SettingsNavigation.swift
180 630 Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutWhenClause.swift
181 627 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
182 623 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Surface/ControlCommandCoordinator+Surface.swift
183 620 cmuxTests/FinderFileDropRegressionTests.swift
184 619 Sources/CmuxEventBus.swift
185 608 Sources/SleepyFaceView.swift
186 608 Sources/TextBoxSubmitActions.swift
187 608 cmuxUITests/FeedSidebarUITests.swift
188 607 Sources/SessionIndexModels.swift
189 604 Packages/macOS/CmuxCommandPalette/Tests/CmuxCommandPaletteTests/CommandPaletteNucleoFFITests.swift
190 601 Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Coordinators/WorkspaceReorderCoordinator.swift
191 594 cmuxTests/PortalTabDragRoutingTests.swift
192 591 Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/DefaultsValueModelLifecycleTests.swift
193 591 cmuxTests/CmuxConfigContextMenuTests.swift
194 588 cmuxTests/CommandPaletteShortcutCustomizationTests.swift
195 586 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttyRuntime.swift
196 586 Sources/JSONCParser.swift
197 585 cmuxTests/SettingsWindowPresenterTests.swift
198 583 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTests.swift
199 581 Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerPrimaryPolicies.swift
200 580 Packages/macOS/CmuxExtensionKit/Tests/CmuxExtensionKitTests/CmuxExtensionKitTests.swift
201 580 cmuxTests/CLIHookNoResponseTests.swift
202 579 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Pane/ControlCommandCoordinator+Pane.swift
203 578 Packages/macOS/CmuxWorkspaces/Tests/CmuxWorkspacesTests/WorkspaceCoordinatorTests.swift
204 577 cmuxTests/AppearanceSettingsTests.swift
205 576 Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileTerminalRenderGrid.swift
206 575 Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Coordinators/WorkspaceGroupCoordinator.swift
207 572 Sources/Feed/FeedTextEditorDebugWindowController.swift
208 567 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift
209 567 Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/ConfigDiscovery/GhosttyConfigDiscovery.swift
210 562 Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/BackingUpPairedMacStore.swift
211 562 cmuxTests/AgentExecutableResolverTests.swift
212 561 cmuxTests/GhosttyConfigPathResolverTests.swift
213 560 cmuxTests/CLISSHPTYResizeInputTests.swift
214 560 cmuxTests/RemoteTmuxControlParserTests.swift
215 559 CLI/CMUXCLI+AgentHookDefinitions.swift
216 558 Packages/macOS/CmuxGit/Sources/CmuxGit/Parsing/GitMetadataService+Config.swift
217 553 Sources/RightSidebarPanelView.swift
218 551 cmuxUITests/AutomationSocketUITests.swift
219 550 Sources/CloudVMActionLauncher.swift
220 549 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Sidebar/ControlCommandCoordinator+SidebarMetadataV1.swift
221 549 Sources/Panels/BrowserAutomation.swift
222 546 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AutomationSection.swift
223 544 cmuxUITests/DisplayResolutionRegressionUITests.swift
224 540 Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileTerminalRenderGridReplay.swift
225 539 CLI/CMUXCLI+Themes.swift
226 539 CLI/CodexTeamsApprovalBridge.swift
227 538 Packages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/MobileTerminalRenderGridTests.swift
228 538 Sources/TerminalController+ControlSurfaceContext2.swift
229 537 Sources/App/WorkspaceRuntimeSettings.swift
230 527 CLI/CLISocketPathResolver.swift
231 527 cmuxTests/BrowserHTTPBasicAuthPromptTests.swift
232 526 Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamStore.swift
233 525 Packages/macOS/CmuxSettings/Sources/CmuxSettings/SocketControl/SocketControlSettings.swift
234 524 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift
235 522 Sources/TerminalPaneDropTargetView.swift
236 520 CLI/CMUXCLI+AmpExtension.swift
237 520 cmuxTests/MainWindowVisibilityControllerTests.swift
238 519 CLI/CMUXCLI+AutoNaming.swift
239 519 Packages/macOS/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/Corpus/stress-two-column-cockpit-sidebar.swift
240 518 Packages/macOS/CmuxSwiftRender/Tests/CmuxSwiftRenderTests/Corpus/stress-git-review-queue-command-deck.swift
241 516 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/CMUXMobileRootView.swift
242 516 Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs.swift
243 516 Sources/TerminalImageTransfer.swift
244 514 Packages/macOS/CmuxSwiftRender/Sources/CmuxSwiftRender/ExpressionEvaluator.swift
245 514 cmuxUITests/UpdatePillUITests.swift
246 511 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Sidebar/ControlCommandCoordinator+SidebarReportsV1.swift
247 510 Packages/iOS/CmuxAgentChatUI/Sources/CmuxAgentChatUI/Transcript/ChatTranscriptTableView.swift
248 510 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Wire/ControlCommandExecutionPolicy.swift
249 509 Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerAdditionalPolicies.swift
250 507 Sources/TerminalControllerTopSupport.swift
251 506 Sources/App/MainWindowVisibilityController.swift
252 504 Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/UserDefaultsSettingsStoreTests.swift
253 504 cmuxTests/TerminalNotificationSocketActionTests.swift
254 503 Sources/Settings/ConfigSource.swift
255 502 Sources/CmuxEventPublishing.swift
256 501 Sources/TerminalNotificationQueue.swift
257 500 Sources/KeyboardShortcutRecorder.swift
-35
View File
@@ -207,9 +207,6 @@ jobs:
- name: Validate Swift warning budget guard
run: ./tests/test_ci_swift_warning_budget.sh
- name: Validate Swift file length budget guard
run: ./tests/test_ci_swift_file_length_budget.sh
- name: Validate auxiliary window close shortcut lint
run: ./tests/test_ci_auxiliary_window_close_shortcuts.sh
@@ -234,38 +231,6 @@ jobs:
- name: Validate bash prompt bootstrap composes with user PROMPT_COMMAND (starship)
run: python3 tests/test_issue_5164_starship_prompt_composition.py
- name: Validate Swift file length budget
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BEFORE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "pull_request" ]; then
BASE_REF="$BASE_SHA"
if MERGE_BASE="$(git merge-base "$BASE_SHA" "$HEAD_SHA")"; then
BASE_REF="$MERGE_BASE"
fi
python3 scripts/swift_file_length_budget.py \
--budget .github/swift-file-length-budget.tsv \
--base-ref "$BASE_REF" \
--merge-ref "origin/${BASE_BRANCH:-main}" \
--merge-head "$HEAD_SHA"
elif [ "$EVENT_NAME" = "push" ] && [ -n "$BEFORE_SHA" ] && ! [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
python3 scripts/swift_file_length_budget.py \
--budget .github/swift-file-length-budget.tsv \
--base-ref "$BEFORE_SHA"
elif git rev-parse --verify --quiet origin/main >/dev/null && MERGE_BASE="$(git merge-base origin/main HEAD)"; then
python3 scripts/swift_file_length_budget.py \
--budget .github/swift-file-length-budget.tsv \
--base-ref "$MERGE_BASE"
else
python3 scripts/swift_file_length_budget.py \
--budget .github/swift-file-length-budget.tsv
fi
- name: Validate feature flags (naming, owners, expiry, single use, no reuse)
run: python3 scripts/lint-feature-flags.py
+2 -2
View File
@@ -72,8 +72,8 @@
"severity": "high"
},
{
"id": "cmux-swift-file-package-boundaries",
"rule": "Flag Swift changes that add too much unrelated responsibility to one file or miss a SwiftPM package boundary: new production Swift files over 400 lines without one responsibility, files over 800 lines, large additions to existing oversized files, mixed UI/state/persistence/network/parsing/protocol code, or independently testable feature logic kept in the app target instead of a small package.",
"id": "cmux-swift-package-boundaries",
"rule": "Flag independently testable or reusable Swift domain logic kept in the app target instead of a small SwiftPM package. Pass for app-lifecycle composition, small UI/AppKit/Ghostty glue, generated or vendored code, prototypes, and tests.",
"scope": [
"**/*.swift",
"**/Package.swift",
+2 -2
View File
@@ -114,8 +114,8 @@
]
},
{
"path": ".github/review-bot-rules/swift-file-package-boundaries.md",
"description": "Source-of-truth cmux lint rule for Swift file size and SwiftPM package boundary review.",
"path": ".github/review-bot-rules/swift-package-boundaries.md",
"description": "Source-of-truth cmux lint rule for SwiftPM package boundary review.",
"scope": [
"**/*.swift",
"**/Package.swift",
+1 -1
View File
@@ -12,7 +12,7 @@ Review production Swift and runtime changes for:
- Fixed sleeps, delays, and polling used as hacky synchronization.
- Legacy concurrency patterns where Swift concurrency is available.
- Incorrect `@concurrent` or `nonisolated async` behavior.
- Swift file sprawl and missing SwiftPM package boundaries for independently testable feature logic.
- Missing SwiftPM package boundaries for independently testable feature logic.
- Production logging that bypasses unified logging or leaks sensitive data.
- User-facing text that is not fully internationalized across every supported app or web locale.
- SwiftUI state and layout patterns that cause stale state, broad invalidation, or render-time mutation.
@@ -6,9 +6,8 @@ import Foundation
import Testing
@testable import CmuxMobileShell
/// Regression tests for `stableMacColorSlots` resets on the account/team
/// boundaries. Split from ``MobileShellCompositePreviewTests`` to keep that
/// file under the Swift file length budget.
/// Regression tests for `stableMacColorSlots` resets on account and team
/// boundaries.
@MainActor
@Suite struct MobileShellCompositeColorSlotResetTests {
@Test func signOutClearsStableMacColorSlots() {
+2 -2
View File
@@ -5,8 +5,8 @@ import UserNotifications
// Notification sound selection, custom sound staging, Focus/DND suppression,
// fallback playback, and notification custom-command execution.
// Extracted from TerminalNotificationStore.swift to keep that file within the
// Swift file length budget.
// Kept separate from TerminalNotificationStore.swift so settings resolution
// and notification delivery have distinct ownership.
enum NotificationSoundSettings {
static let key = "notificationSound"
-564
View File
@@ -1,564 +0,0 @@
#!/usr/bin/env python3
"""Check cmux-owned Swift file lengths against a checked-in budget."""
from __future__ import annotations
import argparse
import pathlib
import subprocess
import sys
DEFAULT_ROOTS = ("Sources", "CLI", "Packages", "cmuxTests", "cmuxUITests")
DEFAULT_THRESHOLD = 500
DEFAULT_INCIDENTAL_GROWTH = 25
DEFAULT_HARD_CAP = 900
IGNORED_PATH_PARTS = (
"/vendor/",
"/ghostty/",
"/homebrew-cmux/",
"/.build/",
"/SourcePackages/",
"/.ci-source-packages/",
)
FileLengthBudget = dict[str, int]
def is_ignored_path(path: pathlib.Path) -> bool:
normalized = "/" + path.as_posix().lstrip("/")
return any(part in normalized for part in IGNORED_PATH_PARTS)
def count_lines(path: pathlib.Path) -> int:
with path.open("r", encoding="utf-8", errors="replace") as handle:
return sum(1 for _ in handle)
def collect_file_lengths(repo_root: pathlib.Path, roots: tuple[str, ...]) -> FileLengthBudget:
budget: FileLengthBudget = {}
for root in roots:
root_path = repo_root / root
if not root_path.exists():
continue
for path in sorted(root_path.rglob("*.swift")):
rel_path = path.relative_to(repo_root)
if is_ignored_path(rel_path):
continue
budget[rel_path.as_posix()] = count_lines(path)
return budget
def is_in_roots(rel_path: str, roots: tuple[str, ...]) -> bool:
return any(rel_path == root or rel_path.startswith(f"{root}/") for root in roots)
def count_blob_lines(content: bytes) -> int:
return content.count(b"\n") + (0 if content.endswith(b"\n") or not content else 1)
def list_tree_swift_paths(repo_root: pathlib.Path, tree: str, roots: tuple[str, ...]) -> list[str]:
result = subprocess.run(
["git", "-C", str(repo_root), "ls-tree", "-r", "--name-only", "-z", tree],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if result.returncode != 0:
raise RuntimeError(result.stderr.decode("utf-8", errors="replace").strip())
paths: list[str] = []
for raw_path in result.stdout.split(b"\0"):
if not raw_path:
continue
rel_path = raw_path.decode("utf-8", errors="surrogateescape")
if not rel_path.endswith(".swift"):
continue
if not is_in_roots(rel_path, roots):
continue
if is_ignored_path(pathlib.Path(rel_path)):
continue
paths.append(rel_path)
return sorted(paths)
def collect_file_lengths_at_ref(repo_root: pathlib.Path, ref: str, roots: tuple[str, ...]) -> FileLengthBudget:
paths = list_tree_swift_paths(repo_root, ref, roots)
if not paths:
return {}
batch_input = "".join(f"{ref}:{rel_path}\n" for rel_path in paths).encode(
"utf-8",
errors="surrogateescape",
)
try:
process = subprocess.run(
["git", "-C", str(repo_root), "cat-file", "--batch"],
input=batch_input,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
except OSError as exc:
raise RuntimeError(str(exc)) from exc
if process.returncode != 0:
raise RuntimeError(process.stderr.decode("utf-8", errors="replace").strip())
budget: FileLengthBudget = {}
offset = 0
stdout = process.stdout
for rel_path in paths:
header_end = stdout.find(b"\n", offset)
if header_end == -1:
raise RuntimeError(f"missing git cat-file header for {rel_path}")
header = stdout[offset:header_end]
header_parts = header.split()
if len(header_parts) == 2 and header_parts[1] == b"missing":
raise RuntimeError(f"missing object for {ref}:{rel_path}")
if len(header_parts) != 3:
raise RuntimeError(f"unexpected git cat-file header for {rel_path}: {header!r}")
try:
size = int(header_parts[2])
except ValueError as exc:
raise RuntimeError(f"invalid git cat-file size for {rel_path}: {header!r}") from exc
content_start = header_end + 1
content_end = content_start + size
if content_end > len(stdout):
raise RuntimeError(f"truncated git cat-file content for {rel_path}")
budget[rel_path] = count_blob_lines(stdout[content_start:content_end])
offset = content_end
if offset < len(stdout) and stdout[offset : offset + 1] == b"\n":
offset += 1
return budget
def tracked_file_lengths(file_lengths: FileLengthBudget, threshold: int) -> FileLengthBudget:
return {
rel_path: line_count
for rel_path, line_count in file_lengths.items()
if line_count >= threshold
}
def count_lines_at_ref(repo_root: pathlib.Path, ref: str, rel_path: str) -> int | None:
try:
result = subprocess.run(
["git", "-C", str(repo_root), "show", f"{ref}:{rel_path}"],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
except OSError:
return None
if result.returncode != 0:
return None
return count_blob_lines(result.stdout)
def parse_budget(text: str, source: str) -> FileLengthBudget:
budget: FileLengthBudget = {}
for line_number, raw_line in enumerate(text.splitlines(), start=1):
line = raw_line.rstrip("\n")
if not line or line.startswith("#"):
continue
parts = line.split("\t", 1)
if len(parts) != 2:
raise ValueError(f"{source}:{line_number}: expected max_lines<TAB>relative path")
count_text, rel_path = parts
try:
count = int(count_text)
except ValueError as exc:
raise ValueError(f"{source}:{line_number}: invalid line count {count_text!r}") from exc
if count < 0:
raise ValueError(f"{source}:{line_number}: line count must be non-negative")
if rel_path in budget:
raise ValueError(f"{source}:{line_number}: duplicate entry for {rel_path!r}")
budget[rel_path] = count
return budget
def load_budget(path: pathlib.Path) -> FileLengthBudget:
return parse_budget(path.read_text(encoding="utf-8"), str(path))
def repo_relative_path(repo_root: pathlib.Path, path: pathlib.Path) -> str | None:
try:
return path.resolve(strict=False).relative_to(repo_root).as_posix()
except ValueError:
return None
def load_budget_at_ref(repo_root: pathlib.Path, ref: str, rel_path: str) -> FileLengthBudget | None:
try:
result = subprocess.run(
["git", "-C", str(repo_root), "show", f"{ref}:{rel_path}"],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
)
except OSError:
return None
if result.returncode != 0:
return None
return parse_budget(result.stdout, f"{ref}:{rel_path}")
def write_budget(path: pathlib.Path, budget: FileLengthBudget) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as handle:
handle.write("# cmux-owned Swift file length budget.\n")
handle.write("# Format: max_lines<TAB>relative path\n")
handle.write("# Reduce counts as files shrink. CI fails if tracked files exceed this budget.\n")
for rel_path, line_count in sorted(budget.items(), key=lambda item: (-item[1], item[0])):
handle.write(f"{line_count}\t{rel_path}\n")
def print_file_summary(label: str, file_lengths: FileLengthBudget) -> None:
total = sum(file_lengths.values())
print(f"{label}: {total} line(s) across {len(file_lengths)} Swift file(s)")
def speculative_merge_tree(repo_root: pathlib.Path, merge_ref: str, merge_head: str) -> str | None:
try:
result = subprocess.run(
["git", "-C", str(repo_root), "merge-tree", "--write-tree", merge_ref, merge_head],
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
except OSError as exc:
print(
f"Speculative merge of {merge_ref} and {merge_head} could not run; "
"falling back to working-tree evaluation.",
)
print(str(exc), file=sys.stderr)
return None
if result.returncode == 0:
tree = result.stdout.splitlines()[0] if result.stdout.splitlines() else ""
if tree:
return tree
print(
f"Speculative merge of {merge_ref} and {merge_head} did not produce a tree; "
"falling back to working-tree evaluation.",
)
if result.stderr.strip():
print(result.stderr.strip(), file=sys.stderr)
return None
if result.returncode == 1:
print(
f"Speculative merge of {merge_ref} and {merge_head} conflicts; "
"falling back to working-tree evaluation.",
)
return None
print(
f"Speculative merge of {merge_ref} and {merge_head} failed; "
"falling back to working-tree evaluation.",
)
if result.stderr.strip():
print(result.stderr.strip(), file=sys.stderr)
return None
def compare_budget(
actual: FileLengthBudget,
allowed: FileLengthBudget,
base_allowed: FileLengthBudget | None,
threshold: int,
all_file_lengths: FileLengthBudget,
repo_root: pathlib.Path,
base_ref: str | None,
incidental_growth: int,
hard_cap: int,
) -> int:
failures: list[tuple[str, int, int | None, str]] = []
incidental: list[tuple[str, int, int, int]] = []
reductions: list[tuple[str, int, int]] = []
for rel_path in sorted(set(actual) | set(allowed)):
actual_count = actual.get(rel_path, all_file_lengths.get(rel_path, 0))
allowed_count = allowed.get(rel_path)
if base_ref and actual_count >= threshold:
base_count = count_lines_at_ref(repo_root, base_ref, rel_path)
if base_count is None:
failures.append((rel_path, actual_count, allowed_count, "new tracked file"))
continue
if base_count < threshold:
failures.append((rel_path, actual_count, allowed_count, "newly tracked file"))
continue
base_growth = actual_count - base_count if base_count is not None else None
if actual_count > hard_cap and base_growth is not None and base_growth > 0:
failures.append((rel_path, actual_count, allowed_count, f"hard cap {hard_cap}"))
continue
if base_growth is not None and base_growth > incidental_growth:
failures.append(
(
rel_path,
actual_count,
allowed_count,
f"PR growth +{base_growth} exceeds incidental allowance {incidental_growth}",
)
)
continue
if allowed_count is None:
failures.append((rel_path, actual_count, None, "missing budget entry"))
continue
base_allowed_count = base_allowed.get(rel_path) if base_allowed is not None else None
if (
base_allowed_count is not None
and allowed_count < base_allowed_count
and actual_count > allowed_count
):
failures.append(
(
rel_path,
actual_count,
allowed_count,
f"budget lowered below actual count (base budget {base_allowed_count})",
)
)
continue
if actual_count > allowed_count and base_growth is not None and base_growth > 0:
incidental.append((rel_path, actual_count, allowed_count, base_growth))
continue
if actual_count > allowed_count:
continue
if actual_count < allowed_count:
reductions.append((rel_path, actual_count, allowed_count))
continue
continue
if allowed_count is None and actual_count >= threshold:
failures.append((rel_path, actual_count, None, "untracked"))
elif allowed_count is not None and actual_count > allowed_count:
failures.append((rel_path, actual_count, allowed_count, "exceeds checked-in budget"))
elif rel_path in allowed and actual_count < allowed_count:
reductions.append((rel_path, actual_count, allowed_count))
if failures:
print("Swift file length budget exceeded.")
print("")
for rel_path, actual_count, allowed_count, reason in sorted(
failures,
key=lambda item: ((item[2] if item[2] is not None else threshold) - item[1], item[0]),
):
comparison_count = allowed_count if allowed_count is not None else threshold
delta = actual_count - comparison_count
if allowed_count is None:
prefix = f"+{delta}" if delta > 0 else "new"
print(f"{prefix} {rel_path}")
print(f" actual={actual_count} budget=untracked threshold={threshold}")
else:
print(f"+{delta} {rel_path}")
print(f" actual={actual_count} budget={allowed_count}")
print(f" reason={reason}")
print("")
print("Split the file, reduce the new growth, or refresh the budget only when accepting known debt.")
return 1
print("Swift file length budget respected.")
if incidental:
print("")
print("Incidental growth allowed by PR gate:")
for rel_path, actual_count, allowed_count, base_growth in sorted(
incidental,
key=lambda item: (item[3], item[0]),
reverse=True,
)[:20]:
print(f"+{base_growth} {rel_path}")
print(f" actual={actual_count} budget={allowed_count} allowance={incidental_growth}")
if reductions:
print("")
print("Budget can be reduced:")
for rel_path, actual_count, allowed_count in sorted(
reductions,
key=lambda item: (item[2] - item[1], item[0]),
reverse=True,
)[:20]:
delta = allowed_count - actual_count
print(f"-{delta} {rel_path}")
print(f" actual={actual_count} budget={allowed_count}")
if len(reductions) > 20:
print(f"... {len(reductions) - 20} more reduction(s)")
return 0
def main(argv: list[str]) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--repo-root",
default=pathlib.Path.cwd(),
type=pathlib.Path,
help="repository root to scan",
)
parser.add_argument(
"--budget",
default=pathlib.Path(".github/swift-file-length-budget.tsv"),
type=pathlib.Path,
help="checked-in file length budget",
)
parser.add_argument(
"--threshold",
default=DEFAULT_THRESHOLD,
type=int,
help="minimum line count tracked by the budget",
)
parser.add_argument(
"--roots",
nargs="+",
default=list(DEFAULT_ROOTS),
help="repo-relative roots to scan",
)
parser.add_argument(
"--write-budget",
action="store_true",
help="write the current file lengths as the budget instead of checking",
)
parser.add_argument(
"--base-ref",
help="git ref used to allow small PR-local growth in files already over budget",
)
parser.add_argument(
"--merge-ref",
help="git ref to speculatively merge with --merge-head before evaluating the budget",
)
parser.add_argument(
"--merge-head",
default="HEAD",
help="git ref for the PR side of a speculative merge",
)
parser.add_argument(
"--incidental-growth",
default=DEFAULT_INCIDENTAL_GROWTH,
type=int,
help="max lines a PR may add to an existing tracked file without refreshing the budget",
)
parser.add_argument(
"--hard-cap",
default=DEFAULT_HARD_CAP,
type=int,
help="absolute max lines for an existing tracked file, even with incidental PR growth",
)
args = parser.parse_args(argv)
if args.threshold < 1:
print("--threshold must be at least 1", file=sys.stderr)
return 2
if args.incidental_growth < 0:
print("--incidental-growth must be non-negative", file=sys.stderr)
return 2
if args.hard_cap < args.threshold:
print("--hard-cap must be at least --threshold", file=sys.stderr)
return 2
if args.write_budget and args.merge_ref:
print("--write-budget cannot be used with --merge-ref", file=sys.stderr)
return 2
repo_root = args.repo_root.resolve(strict=False)
budget_path = args.budget if args.budget.is_absolute() else repo_root / args.budget
merged_tree: str | None = None
if args.merge_ref:
merged_tree = speculative_merge_tree(repo_root, args.merge_ref, args.merge_head)
if merged_tree:
print(
f"Evaluating speculative merge of {args.merge_ref} and {args.merge_head} "
f"(tree {merged_tree})."
)
try:
file_lengths = collect_file_lengths_at_ref(repo_root, merged_tree, tuple(args.roots))
except RuntimeError as exc:
print(f"Error reading Swift files from merged tree: {exc}", file=sys.stderr)
return 2
actual = tracked_file_lengths(file_lengths, args.threshold)
print_file_summary("All scanned cmux-owned Swift files", file_lengths)
print_file_summary(f"Tracked Swift files >= {args.threshold} lines", actual)
budget_ref_path = repo_relative_path(repo_root, budget_path)
try:
allowed = load_budget_at_ref(repo_root, merged_tree, budget_ref_path) if budget_ref_path else None
except ValueError as exc:
print(f"Error reading Swift file length budget: {exc}", file=sys.stderr)
return 2
if allowed is None:
print(f"Missing Swift file length budget: {budget_path}", file=sys.stderr)
return 2
base_allowed: FileLengthBudget | None = None
try:
base_allowed = load_budget_at_ref(repo_root, args.merge_ref, budget_ref_path)
except ValueError as exc:
print(f"Error reading base Swift file length budget: {exc}", file=sys.stderr)
return 2
print_file_summary("Allowed Swift file length budget", allowed)
return compare_budget(
actual,
allowed,
base_allowed,
args.threshold,
file_lengths,
repo_root,
args.merge_ref,
args.incidental_growth,
args.hard_cap,
)
file_lengths = collect_file_lengths(repo_root, tuple(args.roots))
actual = tracked_file_lengths(file_lengths, args.threshold)
print_file_summary("All scanned cmux-owned Swift files", file_lengths)
print_file_summary(f"Tracked Swift files >= {args.threshold} lines", actual)
if args.write_budget:
write_budget(budget_path, actual)
print(f"Wrote {budget_path}")
return 0
if not budget_path.exists():
print(f"Missing Swift file length budget: {budget_path}", file=sys.stderr)
return 2
try:
allowed = load_budget(budget_path)
except ValueError as exc:
print(f"Error reading Swift file length budget: {exc}", file=sys.stderr)
return 2
base_allowed: FileLengthBudget | None = None
if args.base_ref:
budget_ref_path = repo_relative_path(repo_root, budget_path)
if budget_ref_path is not None:
try:
base_allowed = load_budget_at_ref(repo_root, args.base_ref, budget_ref_path)
except ValueError as exc:
print(f"Error reading base Swift file length budget: {exc}", file=sys.stderr)
return 2
print_file_summary("Allowed Swift file length budget", allowed)
return compare_budget(
actual,
allowed,
base_allowed,
args.threshold,
file_lengths,
repo_root,
args.base_ref,
args.incidental_growth,
args.hard_cap,
)
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
-492
View File
@@ -1,492 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT_DIR"
FIXTURE="$TMP_DIR/repo"
BUDGET="$TMP_DIR/budget.tsv"
python3 - "$FIXTURE" <<'PY'
import pathlib
import sys
root = pathlib.Path(sys.argv[1])
def write_lines(path: pathlib.Path, count: int) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("".join(f"line {index}\n" for index in range(count)), encoding="utf-8")
write_lines(root / "Sources" / "Big.swift", 5)
write_lines(root / "Sources" / "Small.swift", 4)
write_lines(root / "Sources" / "vendor" / "Ignored.swift", 100)
write_lines(root / "CLI" / "Tool.swift", 6)
write_lines(root / "Packages" / "Fixture" / "Sources" / "Fixture.swift", 7)
write_lines(root / "Packages" / "Fixture" / ".build" / "checkouts" / "Ignored.swift", 100)
PY
python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 \
--write-budget
git -C "$FIXTURE" init -q
git -C "$FIXTURE" add .
git -C "$FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm baseline
BASE_REF="$(git -C "$FIXTURE" rev-parse HEAD)"
if ! grep -Fq $'5\tSources/Big.swift' "$BUDGET"; then
echo "expected tracked Sources file" >&2
exit 1
fi
if ! grep -Fq $'6\tCLI/Tool.swift' "$BUDGET"; then
echo "expected tracked CLI file" >&2
exit 1
fi
if ! grep -Fq $'7\tPackages/Fixture/Sources/Fixture.swift' "$BUDGET"; then
echo "expected tracked Packages file" >&2
exit 1
fi
if grep -Fq 'Sources/Small.swift' "$BUDGET"; then
echo "small file should not be included" >&2
exit 1
fi
if grep -Fq 'vendor' "$BUDGET"; then
echo "ignored source should not be included" >&2
exit 1
fi
if grep -Fq '.build' "$BUDGET"; then
echo "SwiftPM build output should not be included" >&2
exit 1
fi
python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5
mkdir -p "$FIXTURE/.github"
(
cd "$TMP_DIR"
python3 "$ROOT_DIR/scripts/swift_file_length_budget.py" \
--repo-root "$FIXTURE" \
--budget .github/relative-budget.tsv \
--threshold 5 \
--write-budget
)
if [ ! -f "$FIXTURE/.github/relative-budget.tsv" ]; then
echo "expected relative budget path to resolve inside repo root" >&2
exit 1
fi
if [ -f "$TMP_DIR/.github/relative-budget.tsv" ]; then
echo "relative budget path should not resolve from current directory" >&2
exit 1
fi
python3 - "$FIXTURE/Sources/NewLarge.swift" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
path.write_text("".join(f"new line {index}\n" for index in range(5)), encoding="utf-8")
PY
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 >"$TMP_DIR/new-file.out" 2>&1; then
echo "expected new untracked file failure" >&2
exit 1
fi
if ! grep -Fq 'new Sources/NewLarge.swift' "$TMP_DIR/new-file.out"; then
echo "expected new untracked file output" >&2
cat "$TMP_DIR/new-file.out" >&2
exit 1
fi
if ! grep -Fq 'budget=untracked threshold=5' "$TMP_DIR/new-file.out"; then
echo "expected untracked budget output" >&2
cat "$TMP_DIR/new-file.out" >&2
exit 1
fi
rm "$FIXTURE/Sources/NewLarge.swift"
printf 'new growth\n' >>"$FIXTURE/Sources/Big.swift"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 >"$TMP_DIR/fail.out" 2>&1; then
echo "expected file length budget failure" >&2
exit 1
fi
if ! grep -Fq 'Swift file length budget exceeded' "$TMP_DIR/fail.out"; then
echo "expected budget failure output" >&2
cat "$TMP_DIR/fail.out" >&2
exit 1
fi
if ! grep -Fq '+1 Sources/Big.swift' "$TMP_DIR/fail.out"; then
echo "expected file growth delta" >&2
cat "$TMP_DIR/fail.out" >&2
exit 1
fi
python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 \
--base-ref "$BASE_REF" \
--incidental-growth 1 \
--hard-cap 10 >"$TMP_DIR/incidental.out"
if ! grep -Fq 'Incidental growth allowed by PR gate' "$TMP_DIR/incidental.out"; then
echo "expected incidental growth output" >&2
cat "$TMP_DIR/incidental.out" >&2
exit 1
fi
git -C "$FIXTURE" add .
git -C "$FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm 'allow incidental growth'
UNCHANGED_BASE_REF="$(git -C "$FIXTURE" rev-parse HEAD)"
python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 \
--base-ref "$UNCHANGED_BASE_REF" \
--incidental-growth 0 \
--hard-cap 10 >"$TMP_DIR/unchanged-over-budget.out"
if grep -Fq 'Swift file length budget exceeded' "$TMP_DIR/unchanged-over-budget.out"; then
echo "unchanged over-budget file should pass in base-ref mode" >&2
cat "$TMP_DIR/unchanged-over-budget.out" >&2
exit 1
fi
mkdir -p "$FIXTURE/.github"
printf '6\tSources/Big.swift\n6\tCLI/Tool.swift\n7\tPackages/Fixture/Sources/Fixture.swift\n' >"$FIXTURE/.github/swift-file-length-budget.tsv"
git -C "$FIXTURE" add .github/swift-file-length-budget.tsv
git -C "$FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm 'record checked-in budget'
CHECKED_IN_BUDGET_REF="$(git -C "$FIXTURE" rev-parse HEAD)"
printf '5\tSources/Big.swift\n6\tCLI/Tool.swift\n7\tPackages/Fixture/Sources/Fixture.swift\n' >"$FIXTURE/.github/swift-file-length-budget.tsv"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$FIXTURE/.github/swift-file-length-budget.tsv" \
--threshold 5 \
--base-ref "$CHECKED_IN_BUDGET_REF" \
--incidental-growth 0 \
--hard-cap 10 >"$TMP_DIR/lowered-budget.out" 2>&1; then
echo "expected lowered checked-in budget to fail base-ref check" >&2
exit 1
fi
if ! grep -Fq 'budget lowered below actual count (base budget 6)' "$TMP_DIR/lowered-budget.out"; then
echo "expected lowered-budget reason" >&2
cat "$TMP_DIR/lowered-budget.out" >&2
exit 1
fi
printf 'threshold crossing\n' >>"$FIXTURE/Sources/Small.swift"
printf '6\tSources/Big.swift\n5\tSources/Small.swift\n6\tCLI/Tool.swift\n7\tPackages/Fixture/Sources/Fixture.swift\n' >"$FIXTURE/.github/swift-file-length-budget.tsv"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$FIXTURE/.github/swift-file-length-budget.tsv" \
--threshold 5 \
--base-ref "$CHECKED_IN_BUDGET_REF" \
--incidental-growth 1 \
--hard-cap 10 >"$TMP_DIR/threshold-crossing.out" 2>&1; then
echo "expected below-threshold base file to fail base-ref check" >&2
exit 1
fi
if ! grep -Fq 'reason=newly tracked file' "$TMP_DIR/threshold-crossing.out"; then
echo "expected threshold-crossing reason" >&2
cat "$TMP_DIR/threshold-crossing.out" >&2
exit 1
fi
sed -i.bak '$d' "$FIXTURE/Sources/Small.swift"
rm "$FIXTURE/Sources/Small.swift.bak"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 \
--base-ref "$BASE_REF" \
--incidental-growth 0 >"$TMP_DIR/growth-limit.out" 2>&1; then
echo "expected incidental growth limit failure" >&2
exit 1
fi
if ! grep -Fq 'PR growth +1 exceeds incidental allowance 0' "$TMP_DIR/growth-limit.out"; then
echo "expected growth-limit reason" >&2
cat "$TMP_DIR/growth-limit.out" >&2
exit 1
fi
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$BUDGET" \
--threshold 5 \
--base-ref "$BASE_REF" \
--incidental-growth 1 \
--hard-cap 5 >"$TMP_DIR/hard-cap.out" 2>&1; then
echo "expected hard-cap failure" >&2
exit 1
fi
if ! grep -Fq 'reason=hard cap 5' "$TMP_DIR/hard-cap.out"; then
echo "expected hard-cap reason" >&2
cat "$TMP_DIR/hard-cap.out" >&2
exit 1
fi
printf 'extra growth\n' >>"$FIXTURE/Sources/Big.swift"
printf '7\tSources/Big.swift\n6\tCLI/Tool.swift\n7\tPackages/Fixture/Sources/Fixture.swift\n' >"$TMP_DIR/raised-budget.tsv"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$TMP_DIR/raised-budget.tsv" \
--threshold 5 \
--base-ref "$BASE_REF" \
--incidental-growth 1 >"$TMP_DIR/raised-budget-bypass.out" 2>&1; then
echo "expected raised budget to still fail PR growth check" >&2
exit 1
fi
if ! grep -Fq 'PR growth +2 exceeds incidental allowance 1' "$TMP_DIR/raised-budget-bypass.out"; then
echo "expected raised-budget growth reason" >&2
cat "$TMP_DIR/raised-budget-bypass.out" >&2
exit 1
fi
python3 - "$FIXTURE/Sources/NewBudgeted.swift" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
path.write_text("".join(f"budgeted new line {index}\n" for index in range(5)), encoding="utf-8")
PY
printf '5\tSources/NewBudgeted.swift\n7\tSources/Big.swift\n6\tCLI/Tool.swift\n7\tPackages/Fixture/Sources/Fixture.swift\n' >"$TMP_DIR/new-file-budget.tsv"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$TMP_DIR/new-file-budget.tsv" \
--threshold 5 \
--base-ref "$BASE_REF" >"$TMP_DIR/new-file-budget-bypass.out" 2>&1; then
echo "expected budgeted new large file to fail base-ref check" >&2
exit 1
fi
if ! grep -Fq 'reason=new tracked file' "$TMP_DIR/new-file-budget-bypass.out"; then
echo "expected new-file reason" >&2
cat "$TMP_DIR/new-file-budget-bypass.out" >&2
exit 1
fi
rm "$FIXTURE/Sources/NewBudgeted.swift"
sed -i.bak '$d' "$FIXTURE/Sources/Big.swift"
rm "$FIXTURE/Sources/Big.swift.bak"
printf 'not-a-valid-budget-line\n' >"$TMP_DIR/bad-budget.tsv"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$TMP_DIR/bad-budget.tsv" \
--threshold 5 >"$TMP_DIR/bad.out" 2>&1; then
echo "expected malformed budget failure" >&2
exit 1
fi
if ! grep -Fq 'Error reading Swift file length budget' "$TMP_DIR/bad.out"; then
echo "expected malformed budget error output" >&2
cat "$TMP_DIR/bad.out" >&2
exit 1
fi
if grep -Fq 'Traceback' "$TMP_DIR/bad.out"; then
echo "malformed budget should not print a traceback" >&2
cat "$TMP_DIR/bad.out" >&2
exit 1
fi
printf '5\tSources/Big.swift\n6\tSources/Big.swift\n' >"$TMP_DIR/duplicate-budget.tsv"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$FIXTURE" \
--budget "$TMP_DIR/duplicate-budget.tsv" \
--threshold 5 >"$TMP_DIR/duplicate.out" 2>&1; then
echo "expected duplicate budget failure" >&2
exit 1
fi
if ! grep -Fq 'duplicate entry' "$TMP_DIR/duplicate.out"; then
echo "expected duplicate budget error output" >&2
cat "$TMP_DIR/duplicate.out" >&2
exit 1
fi
if ! git merge-tree --write-tree HEAD HEAD >/dev/null 2>&1; then
echo "Skipping speculative merge-tree budget tests: git merge-tree --write-tree is unsupported by this git."
exit 0
fi
RACE_FIXTURE="$TMP_DIR/race-repo"
python3 - "$RACE_FIXTURE" <<'PY'
import pathlib
import sys
root = pathlib.Path(sys.argv[1])
def write_lines(path: pathlib.Path, count: int) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("".join(f"line {index}\n" for index in range(count)), encoding="utf-8")
write_lines(root / "Sources" / "Racy.swift", 40)
(root / ".github").mkdir(parents=True, exist_ok=True)
(root / ".github" / "test-budget.tsv").write_text("40\tSources/Racy.swift\n", encoding="utf-8")
(root / ".gitattributes").write_text("Sources/Racy.swift merge=keepFeature\n", encoding="utf-8")
(root / "README.md").write_text("base readme\n", encoding="utf-8")
PY
git -C "$RACE_FIXTURE" init -q
git -C "$RACE_FIXTURE" config merge.keepFeature.name 'keep feature side for race fixture'
git -C "$RACE_FIXTURE" config merge.keepFeature.driver 'cp %B %A'
git -C "$RACE_FIXTURE" add .
git -C "$RACE_FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm baseline
git -C "$RACE_FIXTURE" branch -M main
RACE_BASE="$(git -C "$RACE_FIXTURE" rev-parse HEAD)"
git -C "$RACE_FIXTURE" checkout -q -b feature
python3 - "$RACE_FIXTURE/Sources/Racy.swift" <<'PY'
import pathlib
import sys
path = pathlib.Path(sys.argv[1])
path.write_text(
"".join(f"line {index}\n" for index in range(40))
+ "feature line 40\n"
+ "feature line 41\n",
encoding="utf-8",
)
PY
git -C "$RACE_FIXTURE" add Sources/Racy.swift
git -C "$RACE_FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm 'grow racy file'
git -C "$RACE_FIXTURE" checkout -q main
python3 - "$RACE_FIXTURE" <<'PY'
import pathlib
import sys
root = pathlib.Path(sys.argv[1])
(root / "Sources" / "Racy.swift").write_text(
"".join(f"split line {index}\n" for index in range(6)),
encoding="utf-8",
)
(root / ".github" / "test-budget.tsv").write_text("6\tSources/Racy.swift\n", encoding="utf-8")
(root / "README.md").write_text("main readme\n", encoding="utf-8")
PY
git -C "$RACE_FIXTURE" add Sources/Racy.swift .github/test-budget.tsv README.md
git -C "$RACE_FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm 'split racy file and lower budget'
git -C "$RACE_FIXTURE" checkout -q feature
RACE_MERGE_BASE="$(git -C "$RACE_FIXTURE" merge-base main feature)"
python3 scripts/swift_file_length_budget.py \
--repo-root "$RACE_FIXTURE" \
--budget .github/test-budget.tsv \
--threshold 5 \
--base-ref "$RACE_MERGE_BASE" \
--incidental-growth 3 \
--hard-cap 100 >"$TMP_DIR/race-old-behavior.out"
if python3 scripts/swift_file_length_budget.py \
--repo-root "$RACE_FIXTURE" \
--budget .github/test-budget.tsv \
--threshold 5 \
--base-ref "$RACE_MERGE_BASE" \
--merge-ref main \
--merge-head feature \
--incidental-growth 3 \
--hard-cap 100 >"$TMP_DIR/race-merge-ref.out" 2>&1; then
echo "expected speculative merge budget check to catch racy growth" >&2
cat "$TMP_DIR/race-merge-ref.out" >&2
exit 1
fi
if ! grep -Fq 'Sources/Racy.swift' "$TMP_DIR/race-merge-ref.out"; then
echo "expected speculative merge failure to name Racy.swift" >&2
cat "$TMP_DIR/race-merge-ref.out" >&2
exit 1
fi
if python3 scripts/swift_file_length_budget.py \
--repo-root "$RACE_FIXTURE" \
--budget .github/test-budget.tsv \
--threshold 5 \
--merge-ref main \
--write-budget >"$TMP_DIR/race-write-budget.out" 2>&1; then
echo "expected --write-budget with --merge-ref to fail" >&2
exit 1
fi
if ! grep -Fq -- '--write-budget cannot be used with --merge-ref' "$TMP_DIR/race-write-budget.out"; then
echo "expected --write-budget merge-ref argument error" >&2
cat "$TMP_DIR/race-write-budget.out" >&2
exit 1
fi
git -C "$RACE_FIXTURE" checkout -q -b conflict "$RACE_BASE"
printf 'feature readme\n' >"$RACE_FIXTURE/README.md"
git -C "$RACE_FIXTURE" add README.md
git -C "$RACE_FIXTURE" -c user.name='cmux CI' -c user.email='[email protected]' commit -qm 'conflict on readme'
CONFLICT_MERGE_BASE="$(git -C "$RACE_FIXTURE" merge-base main conflict)"
set +e
python3 scripts/swift_file_length_budget.py \
--repo-root "$RACE_FIXTURE" \
--budget .github/test-budget.tsv \
--threshold 5 \
--base-ref "$CONFLICT_MERGE_BASE" \
--incidental-growth 3 \
--hard-cap 100 >"$TMP_DIR/conflict-plain.out" 2>&1
PLAIN_CONFLICT_STATUS=$?
python3 scripts/swift_file_length_budget.py \
--repo-root "$RACE_FIXTURE" \
--budget .github/test-budget.tsv \
--threshold 5 \
--base-ref "$CONFLICT_MERGE_BASE" \
--merge-ref main \
--merge-head conflict \
--incidental-growth 3 \
--hard-cap 100 >"$TMP_DIR/conflict-merge-ref.out" 2>&1
MERGE_REF_CONFLICT_STATUS=$?
set -e
if [ "$PLAIN_CONFLICT_STATUS" -ne "$MERGE_REF_CONFLICT_STATUS" ]; then
echo "conflict fallback should produce the same exit code as plain base-ref evaluation" >&2
echo "plain status: $PLAIN_CONFLICT_STATUS" >&2
echo "merge-ref status: $MERGE_REF_CONFLICT_STATUS" >&2
cat "$TMP_DIR/conflict-merge-ref.out" >&2
exit 1
fi
if ! grep -Fq 'falling back to working-tree evaluation' "$TMP_DIR/conflict-merge-ref.out"; then
echo "expected conflict fallback notice" >&2
cat "$TMP_DIR/conflict-merge-ref.out" >&2
exit 1
fi