22 KiB
type, title, description, tags, sources, generated, verified
| type | title | description | tags | sources | generated | verified | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| engineering quickstart | Quickstart and task routing | Start here to route Termind changes to their owners, preserve platform lifetimes and dependency pins, and choose focused build and test gates. |
|
|
|
|
Quickstart and task routing
Termind is one Xcode project with native TFM (macOS) and TFI (iPhone/iPad) app targets over shared frameworks, plus separate test, widget, and macOS MCP bridge products; platform UI and lifecycle remain app-owned. Source and tests are authoritative. Before changing an ownership boundary, read Cross-Cutting Engineering Invariants and the narrowest topic page below.
Find the owner
| Change | Route and focused check |
|---|---|
| Module placement, imports, nested directories, or shared resources | Module and Product Map, Dependency and Ownership Boundaries, Shared Framework Responsibilities, Native App Responsibilities, and Supporting Products and Tooling; run ArchitectureBoundaryTests for dependency changes. |
| Composition, startup failure, window/scene state, backgrounding, quit | Platform Composition and Lifetimes; use AppTerminationTests, MobileAppTests, MobileWorkspaceSessionTests, or MobileForwardingLifecycleTests for the changed lifetime. |
| Desktop Navigator, sidebar, workspaces, Vault, Assistant layout | macOS UI and Workspace Ownership; target window/navigation or layout tests. |
| Desktop Workspace/Pane sorting, merging, drag-out new windows, or Assistant list reordering | TFM Drag and Drop; preserve the native Workspace List source and live receiver contracts. Use WorkspaceNavigatorDropTests, WorkspaceLayoutDropTests, WorkspaceWindowMoveTests or AssistantConversationListDropTests, plus the source/cancellation/animation suites listed in that guide. |
| File-drop events, Assistant attachments, SSH terminal/SFTP uploads, desktop remote-file dragging or Key import | File Drag and Drop Across TFM and TFI; trace native routing, payload priority, provider staging and transfer lifetimes. Use FileURLDropTargetTests, MobileDropTests, upload/attachment/import suites listed there; the guide also locates the shared Overlay UI. |
| Mobile tabs, scene routes, cards, editors, terminal continuity | iOS and iPadOS UI and Scene Ownership; use MobileWorkspaceSessionTests for window-owned connection/routing changes, MobileAdaptiveLayoutTests or MobileNavigationParityTests for UI changes. For native metrics authentication/navigation use MobileMetricsJourneyTests. |
| Shared UI conventions, fields, tokens, accessibility | UI Design and Shared Presentation; check platform renderings and ArchitectureBoundaryTests. |
| SSH/Telnet, credentials, routes, host-key trust, session cleanup | SSH, Telnet, Trust, and Workspace Session Lifecycle; run connection, trust, authentication, and session tests. For journey route/checkpoint changes, ConnectionJourneyTests covers shared state and MobileConnectionJourneyRenderingTests covers native mobile rendering; see Behavioral Tests and Fixtures. For channel admission and backpressure see SSH Multiplexing and I/O Scheduling. |
| SSH Security Key registration, FIDO2/WebAuthn signing, Vault metadata, or forwarding limits | SSH Security Key Authentication; use FrameworkTests/WebAuthnVaultMetadataTests.swift for destination/jump resolution without Keychain, V45/V2 Vault metadata, legacy storage normalization and failure rollback; TFMTests/WebAuthnSSHTests.swift for assertion/registration validation and SSH wire fields; and FrameworkTests/SSHInteractionPolicyTests.swift for silent-connection restrictions. These fixtures do not replace a hardware-key and native-prompt check. |
| Terminal, metrics, forwards, shell hooks or forwarding pools | Remote Services and Forwarding, Shell Integration, Port Forwarding Dispatch; choose the matching service or pump tests. For metrics keyboard-auth presentation, use MetricsJourneyRenderingTests on desktop and MobileMetricsJourneyTests on mobile. |
| Ghostty terminal menus, touch selection, native surface identity, themes or highlighting | Ghostty Terminal Integration; for menu changes inspect both iOS touch hooks and the separate pointer path, plus macOS secondary-click composition. Run TerminalContextMenuTests and MobileTerminalUploadMenuTests; use the fork touch-selection tests when its menu seam changes. |
| Ghostty fork source, Swift menu hooks, native patches, XCFramework or dependency pin | Ghostty Fork Maintenance; keep the pushed fork revision, submodule gitlink, project revision and resolved pin aligned, and publish a new XCFramework asset first for native patch changes. |
| SFTP browsing, transfer scheduling, external edit | SFTP Transfer, Scheduling, and External Edit Workflow; select the transfer, conflict, or edit suite. |
| Assistant turns, approvals, host tools, skills, memory | Assistant and Agent Runtime; target preparation, cancellation, persistence, tools, or memory tests. |
| SwiftData, secrets, migrations, CloudKit, settings | Persistent Data, Migrations, and Secrets, Settings, Vault, Metrics, History, Device, and Cloud Sync, and CloudKit Release Readiness; select repository, migration, or CloudSyncLifecycleTests / CloudSyncSafetyTests for a sync lifecycle change. |
| MCP server, external upload/download, bridge | MCP Server, Transfers, and Stdio Bridge; run token, server-session, transfer, or bridge tests. |
| StoreKit, localized copy, widgets, screenshots, website | Commerce, Localization, and Product Surfaces, Storefront Commerce Release Checks, Marketing Assets and App Icons, and Build, Test, and Release; use catalog normalization and bilingual validation, app/language, screenshot, StoreKit, or website checks as appropriate. |
| Picking a deterministic test seam | Behavioral Tests and Fixtures; run the narrow behavioral test before the affected target suite. |
Lifetime and change boundaries
TermindDomain defines portable rules; TermindApplication owns ports and use cases; TermindPersistence owns SwiftData models, repositories and container construction; infrastructure implements services; TermindPresentation owns reusable SwiftUI content and copy; apps assemble concrete dependencies and native UI. ArchitectureBoundaryTests checks specific forbidden imports per root, not an all-UI-framework ban: its five-framework rule excludes Presentation and rejects app targets plus AppKit, UIKit, IOKit and ServiceManagement; the separate Presentation rule allows SwiftUI but rejects app targets, AppKit/UIKit, storage, concrete infrastructure and StoreKit. Shared Application cannot import Infrastructure, Persistence or SwiftData; desktop Application/Features and mobile layers have their own checks. The tests also guard concrete constructions in selected feature/application roots, AgentKit type duplication and SwiftData ownership. Fix an ownership violation rather than bypassing the test; see Dependency and Ownership Boundaries for the precise scope.
TermindApp makes the macOS container and shared AppModel at launch; XCTest and debug screenshot demos use in-memory stores. If the local database cannot open, startup fails rather than replacing it with an empty store. Each main WindowGroup instance gets its own WindowModel for selection and workspaces while app services are shared; RootView registers the window, installs its focused scene value and keeps mounted workspace views when switching destinations. The separate external-authentication Window shares the app environment, but removes its manual Window-menu command; it is not another main workspace. TermindIOSApp migrates language preferences and creates one live or testing MobileAppModel; MobileStartupView injects its container on success and shows unavailable data on failure. MobileStartupView waits for the native scene delegate's session ID; MobileBoundSceneRoot then creates a MobileSceneModel in a task, retaining it for that persistent UIKit session rather than constructing observable state during view evaluation. The scene delegate binds to the shared app on appearance; the scene owns navigation and an Assistant presentation model, while its MobileWorkspaceSessionModel owns that window's live workspaces and saved descriptors. The app model aggregates shared services and sessions, not one interchangeable workspace pool across windows. Cold launch consolidates saved descriptors into the first bound session without connecting; selecting one publishes its workspace in that owner's session before connecting. A link received in another window activates the owner rather than making another connection. If a native session truly disappears, reconciliation can transfer its live workspace to an active session without reconnecting; explicit discard closes the affected workspace. Do not persist transports, secrets, one-shot commands or initial paths in descriptors. Desktop session/pane and mobile workspace owners end their own connection-history entries on close. See iOS and iPadOS UI and Scene Ownership and SSH, Telnet, Trust, and Workspace Session Lifecycle.
In Debug XCTest runs, the real desktop app still launches. By default it suppresses automatic launch of the main and external-auth scenes and disables main-scene restoration; an explicit TERMIND_WINDOW_TESTS=1 lifts suppression, as does an active screenshot demo. This host-level safeguard complements, rather than replaces, .requiresVisibleWindow on any test that presents a window or takes focus. Screenshot demos also disable main-scene restoration. See Behavioral Tests and Fixtures and Build, Test, and Release.
Mobile phase changes are aggregated across scenes: background work is paused when all registered scenes are backgrounded; the expiration handler suspends workspaces, stops forwards, pauses transfers, and expires Assistant work. An active scene resumes monitoring, memory, workspaces, eligible sync, and metrics. On macOS, closing the last window does not always quit: during tests it never terminates the test host; otherwise the menu-bar metrics preference and running metrics decide. Quit first rechecks unuploaded SFTP edits and can be cancelled; confirmed termination fences new Assistant runs, drains cancellation/persistence, stops transfers and MCP, then closes agent resources. The delegate replies to AppKit only once, after cleanup or a three-second fallback; the fallback bounds the quit reply, not completion of asynchronous cleanup. Use Platform Composition and Lifetimes for the full sequence and ownership rules.
AgentKit is a separately versioned, host-neutral remote package for the agent loop, approvals, providers, MCP client and built-in tools; Termind owns SSH/SFTP tools and registers them additively. The Xcode project currently requires AgentKit 0.18.1 or later within the next major version; Package.resolved records the selected remote release and revision. For a cross-repository change, test and release ../AgentKit, then update minimumVersion in Termind.xcodeproj/project.pbxproj and re-resolve Termind.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved; the sibling working tree does not affect Xcode's resolved build. Ghostty has a different release boundary: libghostty-spm/ is a maintenance submodule, but Xcode consumes the remote fork at a fixed revision, not that checkout. For package changes push the fork, then update the submodule gitlink, project revision and resolved pin together; native patch changes additionally require a new XCFramework release and matching package URL/checksum. See Ghostty Fork Maintenance for the exact fork sequence and Ghostty Terminal Integration for the native menu hooks. Keep metadata in persistence and secret values behind Keychain references. Follow Dependency and Ownership Boundaries for imports and Build, Test, and Release for release steps.
For CloudKit changes, follow Settings and Cloud Sync through the release checklist: CloudSyncLifecycle owns a separate engine and account scope per category. It commits a recovery fence before making an automatically syncing engine, fetches before authorizing uploads, and reconciles pending engine changes against the durable journal. Pausing, account changes and non-cancellation persistence failures retire the engine and cancel its operations; late callbacks must not act on a replacement engine. Recheck CloudSyncLifecycleTests (including retirement and delegate-context coverage), CloudSyncSafetyTests, and the relevant coordinator tests rather than treating a successful build as a sync migration check.
For connection-journey presentation, shared ConnectionJourney tracks route progress and authentication/host-key checkpoints, while ConnectionJourneyRouteView draws the common route; each app owns its surrounding native prompts and navigation. Test shared state with ConnectionJourneyTests, then the affected app's rendering or metrics-journey fixture; do not treat a rendering fixture as a live SSH test.
Focused validation
Run the nearest behavioral regression first, then the relevant product suite. Always keep -onlyUsePackageVersionsFromResolvedFile so ordinary builds do not silently move the resolved dependency graph:
xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile build
xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile test
xcodebuild -project Termind.xcodeproj -scheme TFI -destination 'generic/platform=iOS Simulator' -onlyUsePackageVersionsFromResolvedFile build
xcrun swift-format lint --strict --recursive Frameworks TFM TFI FrameworkTests TFMTests TFITests TermindMCPBridge
Scripts/check-zh-localization.sh
For TFI tests use an installed iPhone/iPad simulator; initialize libghostty-spm/ with git submodule update --init libghostty-spm only if you need its maintenance checkout (routine pinned builds do not use it). Visible-window macOS suites require .requiresVisibleWindow and explicit TEST_RUNNER_TERMIND_WINDOW_TESTS=1 (or TERMIND_WINDOW_TESTS=1 in the Xcode test action), preferably with -only-testing:TFMTests/<Suite>; the opt-in enables both the test and automatic app-host windows. Opt-in mobile key-window suites require TEST_RUNNER_TERMIND_MOBILE_UI_TESTS=1, -parallel-testing-enabled NO, and a narrow -only-testing:TFITests/<Suite>; MobileAdaptiveLayoutTests and MobileMetricsJourneyTests are serialized but not gated by that variable; use a narrow nonparallel run when working with their process-wide UIKit window fixtures. StoreKit suites require manual authorization and TERMIND_STOREKIT_TESTS=1. Keep these gates off for routine checks; offscreen tests remain enabled. Define app copy via shared TermindPresentation/Strings resources; new app text needs completed zh-Hans and zh-Hant translations in Frameworks/Resources/Localizable.xcstrings. Shared catalogs are manually managed: after catalog edits run Scripts/normalize-string-catalogs.py --write, then Scripts/check-appstrings.sh and Scripts/check-zh-localization.sh (requires jq); run the Chinese check even without intended copy changes, and AppLanguageTests for language behavior. For web changes run cd LandingPage && npm ci && npm run build and check English and /zh/ routes.
Before submitting, inspect the diff for secrets, generated artifacts, cross-layer imports and accidental Package.resolved churn; use the routed operations pages for CloudKit deployment, StoreKit release, marketing assets, and forwarding benchmark measurement semantics.