From d1afa1d744fb4abcccfcefc2c041d781768f04a0 Mon Sep 17 00:00:00 2001 From: Kaede Akino Date: Sat, 19 Sep 2026 09:32:37 +0800 Subject: [PATCH] Compact memory --- Memory/ARCHITECTURE.md | 1669 +++++++++++++++------------------------- Memory/REFACTOR.md | 1182 ++++++++++++---------------- 2 files changed, 1109 insertions(+), 1742 deletions(-) diff --git a/Memory/ARCHITECTURE.md b/Memory/ARCHITECTURE.md index 3161287f..c50c4d50 100644 --- a/Memory/ARCHITECTURE.md +++ b/Memory/ARCHITECTURE.md @@ -1,1156 +1,733 @@ # Termind architecture -Termind is one Xcode project with macOS (`TFM`) and iOS/iPadOS (`TFI`) apps, -shared static frameworks, platform test targets, and a macOS-only MCP bridge. -The agent runtime is the separately versioned `AgentKit` package. +One Xcode project contains macOS (`TFM`), iOS/iPadOS (`TFI`), shared static frameworks, +separate platform/framework test targets, and the macOS-only `termind-mcp` bridge. +`AgentKit` is a separately versioned package. ## Modules and dependency direction -- `TermindDomain` owns platform-independent values and rules. It may use - Foundation, CryptoKit, and AgentKit values, but never UI or concrete I/O. -- `TermindApplication` owns repository ports, feature models, connection policy, - SSH preparation/import previews, remote-file-system interfaces, sync/outbox - coordination, and other use cases. It depends on Domain and AgentKit. -- `TermindInfrastructure` owns in-memory repositories, metrics parsing, StoreKit, - and the shared Vault/Settings/Metrics CloudKit coordinators. It depends on - Application, Domain, and Persistence. -- `TermindRemoteInfrastructure` owns SSH/SFTP, Keychain, key material, remote - sampling, forwarding, transfers, and scoped SSH import reads. Its C libraries - remain outside the portable framework-test graph. It depends on Infrastructure, - Application, and Domain. -- `TermindPresentation` owns reusable SwiftUI content, presentation policy, - Ghostty themes, and the `TerminalPreferences` bridge. It depends only on - Application and Domain; native surfaces, transports, and lifetimes stay in apps. -- `TermindPersistence` exclusively owns every SwiftData model, frozen V1-V22 - schema, migration plan, and container factory. It never depends on an app target. -- `TFM` and `TFI` own platform UI, adapters, and lifecycle orchestration. Both use - `App`, `Application`, `Design`, `DesignSystem`, `Features`, `Infrastructure`, - `Platform`, and `UI`; frameworks never import either app. +| Owner | Responsibilities and dependencies | +| --- | --- | +| `TermindDomain` | Portable values/rules; Foundation, CryptoKit and AgentKit values only, no UI or concrete I/O. | +| `TermindApplication` | Repository ports, feature models, connection policy, SSH preparation/import previews, remote-file-system ports, sync/outbox use cases; depends on Domain and AgentKit. | +| `TermindInfrastructure` | In-memory repositories, metrics parsing, StoreKit, Vault/Settings/Metrics CloudKit coordination; depends on Application, Domain and Persistence. | +| `TermindRemoteInfrastructure` | SSH/SFTP, Keychain, keys, sampling, forwarding, transfers and scoped SSH imports; depends on Infrastructure, Application and Domain. C libraries stay outside the portable framework-test graph. | +| `TermindPresentation` | Reusable SwiftUI content/policy, Ghostty themes and `TerminalPreferences` bridge; depends only on Application and Domain. | +| `TermindPersistence` | All SwiftData models, frozen V1–V22 schemas, migrations and container construction; never depends on an app. | +| `TFM`, `TFI` | Native UI, adapters and lifetimes, organized into App, Application, Design, DesignSystem, Features, Infrastructure, Platform and UI. Frameworks never import apps. | -Frameworks expose explicit public APIs and do not re-export imports. Apps import -canonical owner modules directly, without forwarding aliases or `Shared` type -names. There is no generic Core module. +Use explicit public APIs and direct owner imports: no re-exported imports, forwarding aliases, +`Shared` type names or generic Core module. Shared resources are copied into both app bundles; +icons, metadata and signing remain platform-owned. -Persistence preserves entity identities, fields, defaults, and historical schema -semantics even when code moves. Original-module V1/V14 fixtures guard migration. -`makeContainer` fixes the complete schema and migration plan; each process shares -one container across its scenes. Store failures never silently recreate data. -macOS retains the existing `Termind` store location; mobile uses Application -Support and an in-memory store for tests. Both apps use the same CloudKit container -and bundle-derived Keychain access group, with target-specific signing. +Persistence preserves entity identity, fields, defaults and historical semantics across code moves; +original-module V1/V14 fixtures guard migration. `makeContainer` fixes the complete schema/migration +plan, with one container per process shared across scenes. Failures never silently recreate stores. +macOS retains its `Termind` location; mobile uses Application Support and in-memory test stores. +Both apps use the same CloudKit container and bundle-derived Keychain access group. -Shared localized resources are copied into each app bundle. Platform icons and -metadata remain app-owned. Framework, macOS, and mobile tests stay separate; -architecture tests fail closed when an expected production directory is absent or -empty. Source-based checks are limited to module dependencies, native-list import -allowlists, AgentKit type ownership, and SwiftData model ownership. They do not pin -UI copy, source layout, call-site spellings, retired paths, or shared-component wiring; -those concerns rely on behavioral tests or explicit visual review. +Architecture tests fail closed on missing/empty production roots. Source checks cover only module +dependencies, native-list import allowlists, AgentKit type ownership and SwiftData ownership; +UI copy, source layout, call-site spelling, retired paths and component wiring require behavioral +coverage or visual review. ## State, navigation, and ownership -Single-line UI fields share Presentation's newline-to-space editing policy and -horizontal layout. Binding reads preserve stored values; only edits are normalized. -App-owned native rename editors apply the same policy without interrupting marked -text. Platforms inject app-owned single-line paste adapters so normalization happens -before native controls flatten individual line breaks. Multiline content and secrets -are excluded. Existing validation is unchanged. No persistence migration or bulk -cleanup is performed. +### Editing and saved values -Optional display names share Domain defaults across form hints and Application -save boundaries. Blank or whitespace-only names become concrete Host addresses, -Identity usernames, key algorithm names (`Secure Enclave` for enclave keys), -Host-plus-SSH-argument forwarding names, protocol provider names, or model IDs. -Name hints stay empty when the required source is unavailable: forwarding waits for -a selected Host, and key imports/replacements wait for the current key's algorithm. -Nonempty names, including previously generated ones, remain ordinary stored values; -only explicitly clearing a name requests a fresh default. Key generation, import, -and editing all permit blank names on both platforms. Desktop autosave keeps a -page-owned name buffer so saving a default never interrupts text entry. Reopening -reads the saved value. Legacy blank records and incoming replicas are not backfilled -on load; their next local record save materializes the name in the existing transaction. -No schema or synchronization payload change is required. +Presentation supplies single-line horizontal layout and newline-to-space editing. Reads preserve +stored values; only edits normalize. App-owned paste adapters normalize before native controls +flatten breaks; native rename editors preserve marked text. Multiline content, secrets and existing +validation are unchanged; there is no migration or bulk cleanup. -Mobile provider drafts merge raw model names before materializing defaults at Save. -An explicit clear of a saved name, including a generated name, conflicts with a -concurrent rename. Only a legacy blank name becoming its default in the latest -record is treated as unchanged, so unrelated remote edits still merge normally. +Domain defines optional-name defaults used by form hints and Application saves: Host address, +Identity username, key algorithm (`Secure Enclave` for enclave keys), Host plus SSH argument, +provider protocol name, or model ID. Hints stay empty without their source, including an unselected +forwarding Host or unknown imported/replacement key algorithm. Nonempty names, even generated ones, +remain stored values; clearing requests a fresh default. Key generation/import/editing allow blank +names. Desktop autosave uses page-owned buffers; reopening shows saved values. Legacy/replicated +blank names materialize only on the next local record save, in its existing transaction; no schema +or sync payload changes. Mobile provider drafts merge raw model names before defaulting at Save: +explicit clearing conflicts with a concurrent rename; only a legacy blank becoming its default in +the latest record counts as unchanged. -- Repository snapshots and preference models are application-wide. A window owns - its shell, quick-open, search, navigation, and context-panel state. -- Desktop main scenes each own a distinct `WindowModel`. The process-owned main - window coordinator routes UUID-scoped scene requests and weakly tracks native - windows. Creating a window starts on Welcome; Dock reopening preserves the - existing destination, while the Metrics entry explicitly selects Metrics. - The Dock context menu's New Window action always creates an independent Welcome - window through the same scene factory and activates the app, including when - hidden or without main windows. It is disabled until the factory is ready. - Restoring a scene never restores or reconnects live Workspaces. -- Desktop new-window requests snapshot their source's ordinary content size; - repeated registration cannot reapply it. The native registration bridge sets - the full-size content style before sizing, so titlebar height never accumulates - across copies. Command-N opens Connect in a fullscreen source; explicit Quick - Open New Window still creates a window using its pre-fullscreen size. - The last-used main window's ordinary size is device-local UserDefaults state, - excluded from sync. Fullscreen transitions and auxiliary windows cannot replace - it, and termination freezes it before window teardown. A cold scene uses this - default only when macOS has no individual restored frame; restoring multiple - scenes never applies the shared size over their frames. Tests and screenshot - demos use an in-memory size store. Window positions remain system-owned. -- Desktop Command-W closes the selected Workspace's focused pane first. Closing - the last pane removes its Workspace but keeps the window open. Without a selected - Workspace it requests native window dismissal. All user window-close paths share - SwiftUI's dismissal confirmation whenever that window owns any Workspaces, - including disconnected or hidden ones; cancellation preserves them. Closing a - different window never participates in that decision. Shift-Command-W remains - direct window dismissal, and the existing last-window background policy applies. - Main-window registration suppresses the native document-edited decoration that - SwiftUI couples to dismissal confirmation. Workspaces are not unsaved documents; - the confirmation condition and native dismissal delegate remain unchanged. - The observation follows the registration view's window and ends on detachment. -- A Workspace can move intact to a new window through commands. Navigator drags - use SwiftUI's original UUID string provider for in-window ordering and pane - composition; releasing a drag outside the window never creates a window. - The destination must register before the MainActor transfers ownership; moves - preserve native terminal surfaces, transports, split geometry and session state, - and do not enter close history. Empty source windows remain on Welcome. - Native window closure explicitly disconnects only its owned sessions, cancels - pending moves, and unregisters it. Independent transfers and application-owned - services retain their existing lifetimes. Mounting a session cannot restart - an attempted connection or authentication; explicit retry/reopen still can. -- Paywall presentation is pinned to one main window until dismissed. Speech input - ownership belongs to a composer instance, not a shared conversation ID, so - closing another view of that conversation cannot cancel the recording. -- Desktop navigation starts with Vault and Assistant, places Metrics above Vault - libraries, appends dynamic SSH/SFTP Workspaces, and ends with Settings. Vault - owns Hosts, Keychain, Port Forwarding, Snippets, Known Hosts, Welcome, and their editor routes. -- Desktop main pages share a 360-point detail minimum, independent of the - Assistant's 840-point reading maximum. Opening the Navigator grows the window - when necessary. Vault reserves a 280-point list and a 360–400-point editor, - overlaying the existing editor below 641 points of content width. The covered - list stays mounted at its last visible size. Opening and closing animate the - reserved space and editor position over 0.22 seconds without squeezing its - contents, respecting Reduce Motion. Below 700 points Metrics uses - a titlebar Status/Processes picker with the toolbar title hidden; at larger - widths both panels and the title are visible. - Covered Metrics panels retain their last visible size, table identity, sorting, - and scroll position. Page selection and measured detail width are window-local; - selecting another server resets to Status. Focusing search or narrowing while - search is focused reveals Processes. Compact actions group Pause/Resume into - More, and macOS 15 presents its search field in a toolbar popover. - Resizing changes layout, never route, - editor identity, Workspace composition, or monitoring ownership. -- Assistant conversations are owned by `AssistantLibraryModel`; they are not - Workspace panes. A Workspace owns SSH/SFTP/serial panes and any session-scoped - conversation. A session owns live transports and terminal/browser state. -- Assistant list ordering and pinning are metadata-only operations, independent - of running, approval-waiting, or compaction state. Desktop drag and pin commands - preserve the live conversation instance and commit ordering before publishing - it. Archived chats cannot move, rename editors cannot drag, and archive/delete - retain their active-run and compaction guards. -- Settings data is app-wide but its navigation is window-scoped. Opening Settings - remembers and later restores the replaced destination. -- Feature-specific Application models expose narrow intent surfaces even when they - share one Vault snapshot. `VaultStore` owns the transaction; sessions use - `RemoteSessionHostStore`, never the full mutation API. -- Runtime connection state is never written onto domain records. Saved repository - data is persistent truth; forwarding runs and authentication exist only in memory - and never enter Vault/Metrics/Settings sync or UserDefaults. -- Serial sessions are temporary and local. IOKit/termios objects remain in - Infrastructure; serial paths and parameters are neither persisted nor exposed - as Agent hosts. -- Workspace templates are Vault-synced domain values. They preserve a named - SSH/SFTP/empty-pane split tree and divider fractions, while TFM alone maps - those values to live Workspace sessions and UI. TFI carries the records for - sync but exposes no template surface. Missing saved hosts restore as empty - panes so the stored layout is never collapsed. +### Windows, navigation, and lifetimes -Known Hosts is independent of Host records: editing or deleting a Host does not -change trust. Plain and OpenSSH `|1|` hashed endpoints share one matcher; hashed -records keep their wire `hostname`, and their separate port field is ignored. +- Repository snapshots/preferences are app-wide; shells, quick-open, search, navigation and context + panels are window-owned. Feature Application models expose narrow intents over the shared Vault + snapshot. `VaultStore` owns transactions; sessions receive only `RemoteSessionHostStore`. +- Each desktop scene owns a distinct `WindowModel`. The process coordinator routes UUID-scoped + requests and weakly tracks native windows. New windows start at Welcome; Dock reopening preserves + the destination, Metrics entry selects Metrics. Dock New Window uses the same factory, activates + hidden/windowless apps and is disabled until that factory is ready. Restoration never reconnects + or restores live Workspaces. +- New windows snapshot the source's ordinary content size once. Registration applies full-size + content style before sizing to avoid accumulating titlebar height. Fullscreen Command-N opens + Connect; explicit Quick Open New Window uses the source's pre-fullscreen size. Last-used ordinary + size is device-local UserDefaults, frozen before termination teardown; fullscreen/auxiliary + windows cannot replace it. Cold scenes use it only without a system-restored frame; multiple + restored frames stay distinct. Positions remain system-owned; tests/demos use in-memory sizing. +- Command-W closes the selected Workspace's focused pane, removing an empty Workspace but retaining + its window; otherwise it requests native dismissal. All user window-close paths use SwiftUI + confirmation if that window owns any Workspaces, including hidden/disconnected ones. Cancellation + preserves them; other windows are irrelevant. Shift-Command-W requests direct window dismissal; + the last-window background policy still applies. Main-window registration clears SwiftUI's + document-edited decoration without changing its confirmation condition or dismissal delegate; + observation follows the attached native window and ends on detachment. +- Commands can move a Workspace intact to a registered destination window on MainActor. Navigator + drags keep SwiftUI's UUID-string provider for ordering/composition; dropping outside creates no + window. Moves preserve surfaces, transports, splits and session state without close history; + empty sources show Welcome. Native closure disconnects only owned sessions, cancels pending moves + and unregisters the window. Independent transfers/app services survive. Mounting never restarts + attempted connection/authentication; explicit retry/reopen may. +- Paywalls stay pinned to one main window until dismissed. Speech belongs to a composer instance, + so closing another view of the same conversation cannot cancel it. +- Desktop navigation starts with Vault/Assistant, places Metrics above Vault libraries, appends + dynamic SSH/SFTP Workspaces and ends with Settings. Vault owns Hosts, Keychain, Port Forwarding, + Snippets, Known Hosts, Welcome and editor routes. Settings data is app-wide; window-local Settings + navigation remembers and restores the replaced destination. +- Desktop detail minimum is 360 points; Assistant reading maximum is independently 840. Opening + Navigator grows the window as needed. Vault reserves a 280-point list and 360–400-point editor, + overlaying below 641 points; covered lists retain their last visible size. Editor/space transitions + take 0.22 seconds, preserve content size and respect Reduce Motion. Metrics below 700 points uses + a titlebar Status/Processes picker with hidden title; wider layouts show both panels/title. + Covered panels retain size, table identity, sorting and scroll. Selection/width are window-local; + changing server selects Status, while focusing search or narrowing during search reveals Processes. + Compact Pause/Resume goes in More; macOS 15 search uses a toolbar popover. Resizing changes layout, + never routes, editor identity, Workspace composition or monitoring ownership. +- `AssistantLibraryModel` owns standalone conversations, never Workspace panes. Workspaces own + SSH/SFTP/serial panes and session-scoped conversations; sessions own live transports and + terminal/browser state. Assistant ordering/pinning is metadata-only during runs, approvals or + compaction: preserve instances and commit before publishing. Archived chats cannot move, rename + editors cannot drag, and archive/delete retain run/compaction guards. +- Runtime connection/authentication/forwarding state stays in memory, never on domain records or + in Vault/Metrics/Settings sync or UserDefaults. Serial sessions are temporary/local; Infrastructure + owns IOKit/termios, and serial paths/parameters are neither persisted nor Agent hosts. +- Vault-synced Workspace templates store named SSH/SFTP/empty split trees and divider fractions. + TFM alone maps them to live sessions/UI; TFI only carries records for sync. Missing Hosts restore + as empty panes without collapsing the layout. -SSH imports recursively classify files by content and stage only selectable public -metadata in a window-owned model. Hosts, identities, and keys form an indexed preview: -an imported identity requires its selected key, while a host uses an exact existing or -selected imported identity and otherwise remains unbound. Selecting a host never selects -credentials. The actor reader retains scoped source descriptors and content digests, -never private-key text or passphrases; it rereads selected keys transiently at -unlock/commit and fails closed if a source changed. Picking and drag-and-drop use the -same scoped validation; config parsing never executes commands. Commit writes new -credentials before one Vault save, cleans them up on failure, never overwrites existing -records, and never clears an existing trust mismatch. +Known Hosts is independent of Host records: Host edits/deletes never change trust. Plain and OpenSSH +`|1|` hashed endpoints share a matcher; hashed records retain wire `hostname` and ignore separate port. -App appearance is a global UserDefaults preference applied by the macOS app -delegate through `NSApplication.appearance`; following System clears the override. -Opening at login reflects `SMAppService` on every Settings visit rather than a -mirrored preference. A quiet login hides the app without suppressing its window. -macOS owns an explicit in-app language preference. Mobile delegates language selection -to Apple's per-app Settings and never stores or injects its own locale override. +SSH imports recursively classify content into a window-owned, indexed public-metadata preview. +Imported identities require their selected keys; Hosts use an exact existing/selected imported +Identity or remain unbound. Selecting a Host never selects credentials. The actor reader retains +scoped descriptors/digests, never private-key text/passphrases; selected keys are reread transiently +at unlock/commit and changed sources fail closed. Picking/drop share scoped validation; config +parsing executes no commands. Commit creates credentials before one Vault save, cleans up on failure, +never overwrites records or clears an existing trust mismatch. + +macOS appearance is global UserDefaults applied through `NSApplication.appearance`; System clears +it. Login launch reflects `SMAppService` on each Settings visit, with no mirrored preference; quiet +login hides the app without suppressing its window. macOS owns in-app language selection; mobile +uses Apple's per-app Settings with no stored/injected locale override. ## Remote sessions, terminal, and files -SSH, SFTP, mobile Workspaces, and Metrics share connection preparation and recovery -policy. Interactive host-key approval remains inside the actual transport attempt: -the same socket/libssh2 session continues from handshake through approval and -authentication; callers never perform a separate discovery connection. -Generation tokens prevent callbacks, credential results, and retry timers from a -retired connection mutating its replacement. +### Connection and authentication -Desktop SSH and SFTP share authentication submission resolution and optional -Identity/Host saving through `RemoteSessionHostStore`. Explicit passwords take -precedence over selected-Identity lookup; custom input never inherits an unselected -Identity's password. Saving a selection only replaces an explicitly entered password; -custom input reuses the Host's existing Identity. Identity/secret persistence still -precedes the separate Host association. Sessions retain validation order, passphrase -continuation, prompt/error state, temporary credentials, transient Hosts, and reconnects. +SSH, SFTP, mobile Workspaces and Metrics share preparation/recovery policy. Host-key approval stays +inside the actual socket/libssh2 handshake-to-authentication attempt, never a discovery connection. +Generation tokens reject retired callbacks, credential results and retry timers. -SSH keyboard-interactive challenges use the same authenticated-opening path for -PTY, exec, SFTP, forwarding, key installation, and each jump host. The C callback -waits only on its transport queue while an async, cancellable application responder -presents the server's literal name, instructions, prompts and echo flags. Rounds and -connections have distinct request IDs; answers are ephemeral and never autofilled -from saved passwords, cached, logged, or added to Agent/MCP history. Empty answers -are valid; zero-question rounds complete without requiring a visible form. User -waiting time does not consume libssh2's network timeout. Cancellation wakes the -callback, cancels its responder, and interrupts the pending transport. +Desktop SSH/SFTP share submission resolution and optional Identity/Host saving through +`RemoteSessionHostStore`. Explicit passwords win over selected-Identity lookup; custom input never +inherits an unselected Identity password. Selected credentials change only for explicit password +input; custom input reuses the Host's Identity. Identity/secret writes precede the separate Host +association. Sessions retain validation order, passphrase continuation, prompt/error state, +temporary credentials, transient Hosts and reconnect sequencing. -User connections own their challenge queues. Agent runs reuse their application -interaction queue; macOS background operations and MCP share the native authentication -window with independently cancellable origins. Stopping MCP preserves app-owned -requests. Terminal file drops and all additional transfer connections forbid Termind -credential, passphrase, MFA and host-key forms, including fallback responders on jump -hosts. System key authorization such as Touch ID remains allowed. Username-only and -SSH `none` authentication remain eligible; actual connection success decides availability. -No app-owned mobile authentication sheet exists. Forwarding and Metrics each own -their challenges inside Connection Journey on both platforms; forwarding never uses -the desktop external authentication window. -Cancellation interrupts opening, and temporary-file cleanup must also -remain silent. Cleanup requiring new interaction retains the existing failure report. -Port Forwarding is an independent Vault card library between Keychain and Snippets -on both platforms. Its cards share Hosts' two-row height and show the same name, -address, type, Host and runtime state. A blank forwarding Name previews and saves -the Host name and SSH argument, such as `AWS (-L)`; legacy blank records retain that -display fallback until saved. Renaming the Host does not rewrite a saved rule name. -Desktop Journey lives in -the same Vault detail column and follows its sizing and opening/closing animation. -Its embedded presentation uses 16-point horizontal margins, an icon-only log -button, and vertical scrolling for short windows. Switching between the editor -and Journey fades within that column without replacing the connection or pending -challenge. Other Journey surfaces retain their default presentation. -Saving a desktop forwarding rule closes its editor after persistence succeeds and -keeps the saved card selected; a failed save retains the draft and editor. -Double-clicking a desktop card starts/stops it without -an inline action button. Desktop cards use Vault's window-owned multiple and marquee -selection; the multi-selection context menu offers Stop (disabled without active rules) -and transactional Delete. Starting and retrying always target one rule; neither platform -offers bulk start or Start All/Stop All controls. Mobile pushes a detail page with Start/Stop/Retry in the -trailing toolbar. Host editors never own forwarding drafts; each rule is saved or -deleted in its own rollback-safe Vault transaction and retains its Host reference. -The shared color picker edits a rule's persisted palette value and both card icons -use that color. V20 adds the color with a lightweight migration while freezing the -V1–V19 forwarding entity; legacy JSON defaults to no color. Color travels in the -existing Vault rule payload, with no change to record identity or Host references. -Rules start only through explicit user actions. Startup, foregrounding, saved/synced -configuration changes and failures never open or retry a forwarding connection; the -legacy enabled-rule defaults are unused. Background expiration and app termination -stop all forwarding resources. Navigation does not own transport or prompt lifetimes. +Keyboard-interactive uses the same authenticated opener for PTY, exec, SFTP, forwarding, key +installation and jump hosts. Its C callback waits only on the transport queue for an async, +cancellable responder showing literal server name/instructions/prompts/echo flags. Request IDs +separate rounds/connections. Answers are ephemeral: never autofilled, cached, logged or recorded +in Agent/MCP history. Empty answers are valid; zero-question rounds need no form. Waiting excludes +libssh2 network timeout; cancellation wakes the callback, cancels its responder and interrupts opening. -Forwarding rules sharing a host session share authentication; a new connection must -answer fresh challenges. Stopping one rule preserves its peers; retiring the last -rule cancels the opening and prompts. Listener failures affect only that rule; -transport failure fails all current rules without restarting them. Host route edits -(including jump hosts) and rule replacement stop affected runs. Authentication's own -Continue and Save may continue its explicitly requested opening. Connection and rule -generations reject late callbacks, and stop/start serializes listener cleanup. -Forwarding and jump-host bridges service sockets through DispatchSource readiness -on their owning serial queues; every libssh2 session has exactly one queue owner. -The shared stream pump retains stable write chunks across EAGAIN, uses a 256 KiB -per-direction quantum, caps encrypted reads at 256 KiB per session pass (including -reads performed internally by libssh2 writes), and pauses input at a 2 MiB high -watermark until the queue falls below 1 MiB. A deduplicated FIFO and a 2 ms worker slice prevent a hot flow -from monopolizing its peers. Half-close drains accepted bytes before propagating -EOF. Remote target DNS uses at most four workers; nonblocking TCP connects and a -five-second deadline never block SSH I/O. Channel opens and remote listener starts -retain and retry the library's session-wide operation state. +User connections own challenge queues; Agent runs use their interaction queue. macOS background/MCP +operations share the native authentication window with independently cancellable origins; stopping +MCP preserves app requests. Mobile has no app-owned authentication sheet. Forwarding/Metrics each +own Connection Journey challenges on both platforms, never the desktop external window. -Authenticated forwarding/bridge sessions install an encrypted-output adapter using -libssh2's public send/receive callbacks. It accepts complete encrypted packets and -applies bounded backpressure between library calls, including receive-generated -replies. This avoids the pinned libssh2 forwarded-tcpip confirmation retry losing -its channel on socket EAGAIN. It is installed only after authentication has released -the session abstract context. Teardown switches the adapter to discard writes and -return EOF, allowing session disposal without waiting on the peer. Socket descriptors -are closed only after dispatch-source cancellation handlers retire. Timers serve -keepalive/protocol deadlines, never bulk-data polling; count notifications occur -only on changes, including counts received while a rule is still starting. +Terminal drops and additional transfer connections forbid Termind credential/passphrase/MFA/host-key +forms, including jump-host fallbacks, but permit system key authorization such as Touch ID. Username-only +and SSH `none` remain eligible; connection success determines availability. Cancellation interrupts +opening; cleanup stays silent and reports failure if new interaction would be required. -An Identity may contain only a username: preparation -permits an empty credential list, SSH tries `none`, then advertised methods. Missing -configured secrets and existing authentication failure/retry behavior stay unchanged. +Username-only identities allow empty credential lists: try SSH `none`, then advertised methods; +missing configured secrets and existing failure/retry semantics remain. Device-local Identity +`lastUsedAt` records successful saved-password/private-key reads for setup, not remote authentication. +Resolvers batch destination/jump/proxy reads; missing secrets and availability/editing reads do not +count. First access per Identity/process writes its SwiftData row directly, without Vault journal or +refresh; retries deduplicate. Vault sync strips this metadata and preserves it on remote application. -An Identity's device-local `lastUsedAt` means its saved password or private key was -successfully read for connection setup, not that remote authentication succeeded. -Connection-specific resolvers batch those accesses across destinations, jump hosts, -and authenticated proxies; missing secrets and ordinary Keychain availability or -editing reads never update usage. The first access for each Identity in an app process -is written directly to its SwiftData row without entering the Vault journal or repository -refresh path. Later retries are deduplicated, and Vault sync strips this local metadata -while preserving it when applying remote Identity configuration. +### Port Forwarding -The SSH worker's serial queue owns PTY dimensions. It retains the latest valid -viewport during authentication and uses it for the initial PTY request, including -pixel dimensions. Nonblocking window-change requests retain their original size -through EAGAIN; the pump finishes that packet before sending the newest viewport. -Other channel I/O continues so an earlier blocked write can finish. Disconnect and -connection failure clear resize state, and a retired authentication attempt cannot install a -PTY or clear its replacement's pending dimensions. Ghostty remains the source of -grid measurements; platform layout callbacks never compute remote rows or columns. -Window-change failures are diagnostic logs, not connection failures or UI alerts; -the failed packet is discarded while newer or subsequently requested sizes can proceed. +Port Forwarding is an independent Vault library between Keychain and Snippets. Cards share Hosts' +two-row height and name/address/type/Host/runtime-state fields. Blank names preview/save Host plus +SSH argument, e.g. `AWS (-L)`; legacy blanks use that display fallback until saved. Host renames never +rewrite saved rule names. Rules retain Host references and save/delete in independent rollback-safe +transactions, outside Host editor drafts. V20 adds persisted palette color, freezes V1–V19 forwarding +entities and defaults legacy JSON to no color; existing Vault payload/identity carries it to both icons. -`RemoteFile` is a Domain value, `RemoteFileSystem` the Application port, and the -libssh2 `SFTPClientProtocol` its Remote Infrastructure implementation. File symbols -and localized kind names belong to Presentation. `MobileSFTPSessionModel` owns -path, listing, navigation history, filters, mutations, and stale-request state. +Desktop Journey uses the Vault editor column, sizing/animation, 16-point horizontal margins, icon-only +log action and vertical scrolling. Editor/Journey fades preserve connections/challenges; other Journey +surfaces keep their default presentation. Successful saves close the editor and select the card; +failures retain drafts. Double-click starts/stops without inline action buttons. Desktop uses +window-owned multiple/marquee selection with Stop (disabled without active rules) and transactional +Delete. Start/retry always target one rule; neither platform has bulk start or Start All/Stop All. +Mobile pushes detail with trailing Start/Stop/Retry. -`TerminalCommandDraftController` owns draft requests, timeout, cancellation, and -bracketed-paste payloads. `CommandGenerationCoordinator` adds model timeout, -snapshot freshness, and presentation state. Platforms provide live context, -validity, and insertion only; generated commands are inserted but never submitted. -`TerminalFindController` is shared by desktop SSH/Serial and mobile SSH. +Only explicit actions start/retry rules: launch, foreground, saved/synced edits and failures never do; +legacy enabled defaults are unused. Background expiration/termination stop resources; navigation owns +neither transport nor prompts. Shared host sessions share authentication; new connections require fresh +challenges. Stopping one rule preserves peers; stopping the last cancels opening/prompts. Listener failure +is rule-local; transport failure fails all without restart. Host/jump-route edits and rule replacement +stop affected runs; authentication Continue/Save may continue its requested opening. Connection/rule +generations reject stale results, and stop/start serializes listener cleanup. -Terminal typography is device-local on both platforms. Font family, size, and -size-follow-theme live only in UserDefaults and never enter TerminalPreferences, -the Settings journal, or outgoing CloudKit payloads. Before sync can materialize a -remote snapshot, missing local size keys are seeded from the pre-upgrade local -preferences blob (existing mirrors win), or from the defaults. Legacy remote font -fields are ignored. New terminal payloads omit them; older apps that require those -fields must upgrade to read the new payloads. Theme/custom configuration text keeps -its existing sync behavior, including fonts explicitly authored in that text. -An empty local family means Follow Theme; unavailable installed fonts fall back to -the theme without deleting the selection. Platform font catalogs refresh on Settings -entry and foregrounding. Native font discovery stays in apps, while Presentation -owns the local SwiftUI bindings and final Ghostty configuration. Explicit family -and size overrides follow theme/custom commands; the family list is reset before -applying the selected family. Changes reconfigure existing surfaces in place. +Forwarding/jump bridges use DispatchSource readiness and exactly one serial queue per libssh2 session. +Stream pumps retain write chunks across EAGAIN, use a 256 KiB per-direction quantum and 256 KiB/session-pass +encrypted-read cap (including reads inside writes), and pause input at 2 MiB until below 1 MiB. A deduplicated +FIFO/2 ms slice provides fairness; half-close drains accepted bytes before EOF. Target DNS has at most four +workers; nonblocking TCP has a five-second deadline. Channel opens/remote listener starts retain and retry +session-wide library operation state. -macOS SSH and serial terminals share two default-off mouse preferences: copy a -completed mouse selection and paste on right-click. They use the existing terminal -Settings sync record and UserDefaults mirror; missing fields in older records decode -as false. Mobile preserves the values without exposing or applying them. Preference -changes update the persistent native view without rebuilding its Ghostty surface. -Only a local mouse selection completion triggers automatic copying. A right-click -chooses its route on press and pastes once on release through Ghostty's clipboard -binding inside interactive input capture, preserving bracketed paste and broadcast -routing. Remote mouse capture and Option-right-click retain Ghostty's original -handling, and mouse reports never enter the broadcast input capture scope. -Empty clipboard pastes do not arm deferred input capture. A captured bracketed paste -retains its classification across callback chunks until its closing marker; an -interrupted sequence expires with the existing short-lived input ticket window. +After authentication releases the abstract context, public send/receive callbacks install an adapter +accepting whole encrypted packets with bounded backpressure between library calls, including generated +replies. This avoids the pinned libssh2 forwarded-tcpip confirmation losing channels on socket EAGAIN. +Teardown discards writes/returns EOF; descriptors close only after dispatch-source cancellation handlers. +Timers handle keepalive/protocol deadlines, never bulk polling. Count notifications fire only on changes, +including while starting. -Host Shell Integration is a Vault-synced three-state override: Follow Global Settings, -Enable, or Disable. V22 adds its backing column without changing V1–V21 Host schemas; -legacy stores and payloads default to following global settings. Both Host editors -place Integration after Group (Group & Tags on desktop), show Enable / Follow Global Settings / -Disable with the default in the middle, and retain explicit-save drafts. -Each SSH connection attempt resolves the destination Host override and global terminal -preference once, before authentication, and retains that value for bootstrap, History, -Command Generator, remote system information, and integration-dependent directory actions. -Local or synchronized Host/global edits never change a current connection. New connections -and manual/automatic reconnects resolve the latest values; edits never initiate reconnects -or inject scripts into an existing shell. SFTP and forwarding route identity is unchanged. +### Terminal behavior -Mobile SSH sends shell-integration and transport-exit callbacks through one ordered, -session-scoped MainActor consumer. Close, reconnect, or background expiration ends -it. Authenticated installation and command completion establish prompt readiness; -following prompt markers are idempotent. Bash DEBUG fallback passes `BASH_COMMAND` -and ignores Termind's `__termind_*` helpers so draft protocol traffic never creates -History rows or marks the prompt busy. +The SSH worker queue owns PTY dimensions: retain the latest valid authentication-time viewport, including +pixels, for initial creation. Window-change packets keep their original dimensions through EAGAIN, finish +before the newest request, and allow other I/O to progress. Disconnect/failure clears resize state; retired +authentication cannot install a PTY or clear replacement dimensions. Ghostty alone computes the grid. +Resize failures log diagnostics, discard that packet and permit later sizes; they never fail connections +or alert users. -Command History begins only after an authenticated start event. Completion may add -remote exit status and duration; both remain optional and duration is never inferred -from client receive time. Explicit backslash continuations are stored with each -boundary normalized to one space; other line breaks remain part of the command while -desktop and mobile History rows show only the first line with an ellipsis. History is -capped at 1,000 rows per host. +Application's `TerminalCommandDraftController` owns requests, timeout/cancellation and bracketed paste; +`CommandGenerationCoordinator` adds model timeout, freshness and presentation. Apps supply context, +validity/insertion; generated commands are inserted, never submitted. `TerminalFindController` serves +desktop SSH/Serial and mobile SSH. -SFTP History is device-local persisted truth shared across apps. Directories are -recorded only after a fresh successful listing and files only after download. -Failed, cancelled, stale, missing, or type-changed operations do not update it. -Transfers retain generation-scoped `SFTPTransferSession` ownership independently of -browser panes. The first additional connection probes the complete SFTP route silently; -success supplies a worker and enables concurrent silent opens within the existing limits. -Failure selects serial reuse of the authenticated browser connection. Later silent failures -degrade pending work without replaying active file operations. Probe results are in-memory, -shared within that generation, and cancellation does not cache a negative result. Terminal -uploads use their successful silent preflight connection for the first transfer. Durable -host trust is refreshed after initial authentication, but once-only approval is not promoted -to trust for additional connections. No credential presence or persistence flag gates -transfer eligibility or parallelism. -The application-wide queue defaults to 15 active file transfers and 5 per Host.ID, -including transfers from multiple terminal/browser sessions for that host. Global -limits are configurable from 1 to 100; per-host limits from 1 to min(50, global). -`SFTPPreferences` owns normalization. Each app wires its `SFTPSettingsModel` to the -coordinator independently of Settings view lifetime; limit changes affect later -scheduling and never interrupt already active transfers. -Closing a pane releases only its ownership. Queued jobs retain the primary connection until -completion/cancellation; paused jobs retain their configuration/checkpoint but release their -connection ownership. Retired browser generations never replace transfer snapshots. Copies -whose endpoints share one connection alternate bounded reads and writes on that worker, -checking source size/metadata and EOF before the existing atomic temporary-file commit. -Terminal preflight and batch preparation are cancellable and scoped to the terminal -generation; late results cannot enqueue files after invalidation. -Terminal file-drop failures, including interactive-authentication requirements and mobile -file-provider staging failures, use the existing nonmodal upload overlay on both platforms. -Rejections remain visible for three seconds; queued notices last two seconds and cannot -replace a visible failure from the same batch. New drops retire earlier preparation and -notice timers. SFTP browser operation alerts and transfer conflict choices remain app-owned. -Upload/download checkpoints, file references, acknowledged offsets and incremental digests live only -in memory. Pause and connection loss retain the destination partial file and release -the connection; explicit resume verifies the source and partial prefix before seeking. -Ordinary transfers add no source staging, bookmark persistence, or restart recovery. Cancellation -and host deletion remove partial files, reporting a path if cleanup fails. Mobile -background expiration pauses uploads/downloads and cancels remote copies. macOS termination gives asynchronous -cleanup at most three seconds, so an unreachable server cannot hold up quitting. -Nothing is silently replayed. -Desktop and mobile upload/download and remote-copy preparation and enqueueing use -Remote Infrastructure planners. Download batches retain their destination independently -of file jobs, so directory-only selections still create their empty directory trees. -Remote-copy enqueueing reports directory mutations so desktop refreshes also cover -copies without file jobs. App sessions retain pending-batch presentation, cancellation, -error handling, and completion refresh; interactive browser deletion stays app-owned. -Upload and remote-copy conflict resolution share one Remote Infrastructure resolver -over `RemoteFileSystem`. Upload retains strict-descendant preflight counting while -copy also suppresses the conflicting root itself. Both use the same subtree mapping, -Keep Both naming, cancellation checkpoints, and recursive child-name/path validation; -created-directory accounting remains available to awaited copy outcomes. +Font family/size/follow-theme-size are device-local UserDefaults, excluded from `TerminalPreferences`, +Settings journals and CloudKit. Before remote materialization, seed missing local size keys from the +pre-upgrade local blob or defaults; existing mirrors win. Ignore legacy remote font fields and omit them +from new payloads (older readers requiring them must upgrade). Theme/custom text still syncs, including +authored fonts. Empty family follows theme; unavailable fonts fall back without deleting selection. +Apps refresh native catalogs on Settings entry/foreground; Presentation owns local bindings/final Ghostty +configuration. Apply explicit overrides after theme/custom commands, resetting family lists first; +reconfigure live surfaces in place. -macOS SFTP file-row context menus are AppKit snapshots built on the context click, -after reconciling selection. Their targets and native menu items survive SwiftUI -row reconfiguration from hover, focus, selection, and directory refreshes. Menu -presentation never mutates selection from a SwiftUI appearance callback. +macOS SSH/Serial have default-off copy-completed-mouse-selection and right-click-paste settings in the +existing synced terminal record/defaults mirror; missing fields decode false. Mobile preserves without +exposing/applying them. Changes retain native views/surfaces. Only local selection completion copies. +Right-click chooses routing on press, pastes once on release through Ghostty clipboard binding inside +interactive input capture, retaining bracketed paste/broadcast. Remote mouse capture and Option-right-click +keep Ghostty handling; mouse reports stay outside broadcast capture. Empty paste arms no deferred capture; +bracketed-paste classification spans chunks through its terminator or existing short-lived ticket expiry. -SFTP external edits have one process-local Application model per app, independent of -browser/workspace lifetime. Remote Infrastructure owns managed local copies, streamed -SHA-256 checks, file/directory observation (including atomic-save replacement), and -immutable upload snapshots. Snapshots are an edit-specific exception to ordinary -transfers' no-staging rule; paused uploads retain them and terminal completion removes -them. Editing never automatically uploads. Only changed records appear in the floating -bottom overlay; clean records remain internal so repeated opens reuse their local copy. -The edit card overlays the full-height file viewport with native Liquid Glass on OS 26 -and later, and native material on older systems. Its row container stays transparent and -the rounded glass is a separate backdrop, so file rows remain visible around and through -the floating card. Only the scroll content gets a bottom inset, so files scroll behind the -glass while the last row can still clear it; no blank safe-area region is reserved for the -card. -Discard stops tracking and preserves the local copy for any external editor; the next -remote open downloads a fresh copy. iOS exposes Discard through native trailing swipe -actions, and both platforms expose it in the row menu. Running/preparing uploads cannot -be discarded; paused uploads are cancelled and cleaned up before their record is removed. -Hosts own activation, background, termination and native preview/open adapters. -The macOS edit menu uses ordinary menu actions; Keep Local Copy delegates to an app-owned -save panel and coordinated file copy instead of embedding ShareLink in the native menu. -macOS checks edits before quitting; iOS cannot promise a quit warning or recovery after -process termination. Background expiration still pauses uploads, with explicit resume. +Host Shell Integration is Vault-synced Follow Global Settings/Enable/Disable; V22 adds a column while +freezing V1–V21 Hosts and defaults legacy data to Follow Global. Both explicit-save editors place it after +Group (desktop Group & Tags), with Follow Global centered between Enable/Disable. Each SSH attempt resolves +destination override/global preference once before authentication for bootstrap, History, Command Generator, +system info and directory actions. Live edits never inject scripts/reconnect/change that attempt; new/manual/ +automatic reconnects resolve again. SFTP/forwarding route identity is unchanged. -Edit upload policy validates the original destination's kind, size, modification time -and mode both before writing and immediately before remote rename, preserving its -permission bits on the temporary file. Overwriting commits use the OpenSSH POSIX rename -extension; standard rename is used only when the extension is unsupported. Ordinary -non-overwriting renames never use that extension, and failures never unlink the original -as a replacement workaround. This is a best-effort metadata guard, not atomic -compare-and-swap against unrelated remote writers. Missing metadata or changed/missing -files require explicit confirmation; nonregular destinations fail closed. Successful -commit acknowledges only the snapshot digest, so later saves remain dirty. Host removal -or route changes invalidate old records without deleting their editable local copies. -There are no extension allowlists: SHA-256 compares all regular-file contents. The iOS -Quick Look delegate enables native editing where supported, receives in-place updates -and takes ownership of temporary edited copies. Different-format copies are exported -separately instead of overwriting the original remote format. Shared Presentation owns -the dirty-file card; AppKit/UIKit and their lifecycle adapters stay in their apps. +Mobile integration/exit callbacks share one ordered session MainActor consumer, ended by close/reconnect/ +background expiration. Authenticated installation and command completion establish prompt readiness; +later prompt markers are idempotent. Bash DEBUG fallback passes `BASH_COMMAND` and ignores `__termind_*` +helpers, preventing draft protocol traffic from creating History or marking the prompt busy. -Open With uses the same managed edit copy as ordinary Open. macOS discovers applications -by UTType and launches the one selected for that invocation without changing defaults. -iOS retains Quick Look as its primary action and uses a workspace-owned document -interaction controller for Open With; retired/background presentation requests cannot -surface a late download in another workspace. The controller survives scene deactivation -until an in-progress document handoff finishes. Receiving apps may edit an independent -copy, so mobile offers explicit same-format edited-copy import into an existing record. -Import preserves the original remote identity and baseline, rejects invalidated/busy -records, and only marks local changes; the existing explicit upload/conflict flow commits them. +Command History starts only after authenticated start. Remote exit status/duration are optional; duration +is never inferred from receive time. Backslash-continuation boundaries normalize to one space; other +newlines remain stored, while both UIs show first-line-plus-ellipsis. Limit: 1,000 rows per Host. -`sftp_copy` is Termind-owned and acting-only in Assistant, with Ask/sequential -policy. It copies regular files and directory trees into an existing directory; -unsupported descendants fail before writes. Its awaited operation shares browser -manifest and conflict helpers, including directory merging and destructive type -replacement. Source data is never deleted. Batches are not atomic: failures retain -completed items and disclose the failing path and completed counts. -Two dedicated SFTP leases use the same authenticated connection opener as other -Agent tools, but are neither pooled nor idle-swept. Their shared cancellation token -is cancelled by host deletion, transport shutdown, or caller cancellation. Leases -remain tracked while opening and are always disconnected after use. Credential -retries apply only to acquisition, never to an operation that has started. -Socket-relayed copies require successful source EOF and an exact byte count before -the remote temporary file is committed. Cancellation shuts down the relay to unblock -socket I/O. No scratch staging, local temporary file, or model-visible payload is used. +### SFTP transfers and external edits -The macOS MCP Server exports the same `sftp_copy` definition without a scratch -workspace. Its existing Bearer token/tool switches and Full access execution remain -the authorization boundary; SSH credentials and host keys still use external -authentication. The tool is annotated as state-changing, potentially destructive, -non-idempotent, and open-world. Session termination/server shutdown cancels and awaits -active copies. Audit consumes byte progress and final counts only, never file bodies; -the summary records both endpoints and the conflict policy. The stdio bridge remains -a protocol-only forwarder. Mobile exposes Assistant tools but no local MCP listener. +`RemoteFile` belongs to Domain, `RemoteFileSystem` to Application, and libssh2 `SFTPClientProtocol` to Remote +Infrastructure; symbols/localized kinds belong to Presentation. `MobileSFTPSessionModel` owns paths, listings, +history, filters, mutations and stale-request state. Persisted device-local SFTP History records directories +only after fresh successful listings and files only after downloads; failed/cancelled/stale/missing/type-changed +operations never update it. -The macOS MCP server also owns a separate HTTP-to-SFTP byte channel. `sftp_upload` -issues a ten-minute, session-bound 256-bit capability bound to its MCP session, host, -full remote path, size, SHA-256, and overwrite intent. `sftp_upload_status` reads -only that session's bounded in-memory state. Upload PUTs authenticate their own -capability before consuming the body; ordinary MCP retains its 4 MiB body limit -and long-lived Bearer authentication. Both routes remain loopback-only. Capabilities -are headers, never query parameters, durable audit data, or long-lived credentials. +Generation-scoped `SFTPTransferSession` outlives panes. Its first additional connection silently probes the +complete route: success becomes a worker/enables bounded concurrent silent opens; failure serially reuses the +authenticated browser connection. Later failures degrade pending work without replaying active operations. +Results are generation-local/in-memory; cancellation never caches failure. Terminal preflight supplies its +successful connection to the first transfer. Refresh durable trust after initial authentication, without +promoting once-only approval. Credential presence/persistence never gates eligibility or parallelism. -HTTP bodies are consumed in bounded chunks with socket-relay backpressure and no -local file staging or scratch access. Remote Infrastructure owns the HTTP-agnostic -stream operation and dedicated SFTP lease. Exact byte count and incrementally -verified SHA-256 authorize source EOF; only then may the remote temporary file be -closed and committed. Existing directories cannot be overwritten. Failed cleanup -reports the potentially remaining remote temporary path. Interrupted writes retain an in-memory checkpoint and a remote partial file. -Status exposes `upload_offset`, counting only acknowledged remote writes. A sequential -retry must send `Upload-Offset` equal to it and a Content-Length covering the remaining -suffix; parallel claims are rejected. The full-file digest includes the verified -prefix. Completed writes are never replayed. Session retirement, host deletion, tool disablement, server shutdown, and -token rotation cancel live work; active uploads prevent idle session expiry. -A completed remote commit stays successful even when its HTTP response is lost. -Retained upload completions also keep an otherwise idle session queryable for -thirty minutes; terminal records are globally bounded to 256 entries. -Metadata-only HTTP upload audit rows are independent of ticket-creation audit rows. -No sandbox entitlement, persistence schema, or AgentKit dependency change is needed. +The app-wide queue defaults to 15 active files, five per Host.ID across all sessions; limits are 1–100 global +and 1–min(50, global) per host. `SFTPPreferences` normalizes; each app wires `SFTPSettingsModel` to scheduling +outside Settings view lifetime. Changes affect future scheduling, never active work. Closing panes releases +only their ownership; queued jobs retain the primary connection, paused jobs retain configuration/checkpoints +but release connections. Retired browser generations cannot replace snapshots. Same-connection copies alternate +bounded reads/writes, validate size/metadata/EOF, then atomically commit temporary files. -`sftp_download` issues the reverse resumable HTTP capability, bound to the session, -host, full remote path, and optional expected SHA-256. Ticket creation performs no -remote I/O. GET authenticates and atomically claims before connecting; it supports -regular files only, with no query credentials, request bodies or HEAD. A single -open-ended `Range: bytes=N-` returns 206 and Content-Range, while an out-of-bounds -offset returns 416 without consuming the ticket. The client chooses N from its -actually saved bytes, not server send progress. A bounded prefix reread verifies -previously read bytes before response headers and reconstructs the full-file digest; -source metadata changes or prefix mismatches reject resumption. A dedicated SFTP lease opens a checked `SFTPDownloadStream`; the worker -owns its handle IDs, native pointers, and queue. FSTAT on the opened handle fixes -the reliable declared size, and EOF verifies exact byte count plus the final size -and available modification time. This is not an atomic remote-file snapshot. +Terminal preflight/preparation is cancellable and generation-scoped; late results cannot enqueue. Drop errors, +including interaction requirements/mobile file-provider staging, use nonmodal upload overlays: failures stay +three seconds, queued notices two and cannot replace same-batch failures. New drops retire prior preparation/ +notice timers. Browser alerts and conflict choices stay app-owned. -The HTTP file-response body is pull-based, reading at most 64 KiB only after the -previous network send completes; it never uses the SSE AsyncStream or local -staging. SHA-256 covers actual transmitted bytes. A terminal success requires -source validation, full HTTP sending, checked handle closure, and dedicated lease -release. Pre-header failures return safe JSON; post-header failures close the -connection without appending JSON or changing the announced status. Clients must -poll `sftp_download_status`, compare local size/hash, and finalize their own file; -HTTP 200 and Content-Length alone are not proof of success or local persistence. +Checkpoints, file references, acknowledged offsets and incremental digests are memory-only. Pause/connection +loss retains partial destinations and releases connections; explicit resume verifies source and partial prefix +before seeking. Ordinary transfers add no staging, persistent bookmarks or restart recovery. Cancellation/Host +delete removes partials, reporting failed cleanup paths. Mobile expiration pauses upload/download and cancels +copies; macOS quit allows at most three seconds for async cleanup. Nothing replays silently. -Upload and download compose the same MCP ticket metadata value and retention policy. -The shared policy identifies expired waiting/paused tickets and prunes completions; -each direction still owns its live states, claim rules, streaming, and cancellation. -Upload expiry can start asynchronous partial-file cleanup; download success still -permits a valid Range request. Each direction keeps its own ticket dictionary. +Remote Infrastructure planners serve both apps' upload/download/copy preparation/enqueueing. Download batches +retain destinations independently of file jobs so empty trees are created; copy enqueueing reports directory +mutations for refresh even without files. Apps keep pending-state UI, cancellation/errors/completion refresh +and interactive deletion. `SFTPRemoteConflictResolver` shares subtree mapping, Keep Both, cancellation and +validated recursive child paths over `RemoteFileSystem`: upload preflight blocks strict descendants; copy +also blocks the conflicting root. Awaited copies retain created-directory counts. macOS file-row menus are +AppKit context-click snapshots after selection reconciliation; targets/items survive SwiftUI reconfiguration. +Menu appearance callbacks never change selection. -Upload and download share one four-active-transfer budget. Each direction retains -at most 64 unfinished capabilities (including paused attempts) and 256 terminal -records for thirty minutes. HTTP disconnect and recoverable SSH failure pause a -transfer; explicit revocation still cancels it. Retries require the original active -session and unexpired capability, and neither checkpoints nor capabilities survive -process exit. Generation IDs prevent old transport callbacks affecting a new attempt. Both -directions share session/host retirement, server-stop, token-rotation, and activity -boundaries; disabling one direction revokes it immediately. Capacity is released -only after cleanup. HTTP download audit rows are independent of MCP ticket calls -and retain only endpoint metadata, sent byte counts, and terminal state, never -file bytes or capability tokens. The existing upload and stdio bridge remain intact. +Each app has one process-local Application edit model independent of browsers/Workspaces. Remote Infrastructure +owns managed copies, streamed SHA-256, file/directory observation (including atomic replacement) and immutable +upload snapshots. Edit snapshots are the staging exception: pauses retain them, terminal completion removes +them. Upload is always explicit. Only dirty records appear in the shared floating bottom card; clean records +remain reusable. Full-height files scroll behind a separate rounded Liquid Glass (OS 26+) or native-material +backdrop with transparent rows; only scroll content gets a bottom inset, never a blank reserved safe area. + +Discard stops tracking but preserves the editor's local copy; next Open downloads anew. Both row menus and +iOS trailing swipe expose it. Running/preparing uploads cannot discard; paused uploads cancel/clean first. +Apps own activation/background/termination and native open/preview. macOS ordinary edit-menu actions use an +app save panel/coordinated copy for Keep Local Copy, never ShareLink; macOS checks edits before quit. iOS +promises neither quit warnings nor post-termination recovery; background-paused uploads require explicit resume. + +Edit uploads validate original kind/size/mtime/mode before writing and before rename, preserving permissions. +Overwrite uses OpenSSH POSIX rename, falling back to standard rename only if unsupported; non-overwriting +rename never uses it, and failure never unlinks the original as a workaround. This metadata guard is best-effort, +not atomic compare-and-swap. Missing/changed metadata or files require confirmation; nonregular destinations +fail closed. Success acknowledges only the snapshot digest, so later saves remain dirty. Host removal/route +edits invalidate records but preserve copies. All regular files use SHA-256 without extension allowlists. +Quick Look enables supported editing, receives in-place updates and owns temporary edited copies; different +formats export separately. Presentation owns the card; native adapters remain app-owned. + +Open With reuses the managed copy. macOS discovers via UTType and launches the invocation's choice without +changing defaults. iOS keeps Quick Look primary and uses a Workspace-owned document controller, rejecting +retired/background late-download presentations in other Workspaces while retaining in-progress handoffs across +scene deactivation. Since receivers may edit independent copies, explicit same-format import targets an existing, +valid/nonbusy record, preserving remote identity/baseline and only marking dirty; normal upload/conflict policy commits. + +### Agent and MCP file channels + +Termind's acting-only `sftp_copy` uses Ask/sequential policy. It copies regular files/trees to an existing +directory, rejects unsupported descendants before writing, and shares browser manifest/conflict handling, +including directory merge/destructive type replacement. Sources remain intact. Batches are non-atomic; +failures disclose retained completed items/counts and the failed path. Two dedicated, unpooled, unswept SFTP +leases share the Agent authenticated opener and a cancellation token for caller/Host deletion/transport shutdown; +track them during opening and always disconnect. Credential retries stop after acquisition. Socket relays require +EOF/exact bytes before temp-file commit; cancellation unblocks sockets. No staging/scratch/model-visible bytes. + +macOS MCP exports the same definition without scratch. Bearer/tool switches and Full access remain authorization +boundaries; credentials/host keys use external authentication. Annotations are state-changing, potentially +destructive, non-idempotent and open-world. Session/server termination cancels and awaits copies. Audit records +byte progress/final counts, endpoints and conflict policy, never bodies. The stdio bridge only forwards protocol; +mobile offers Assistant tools without a local MCP listener. + +macOS also provides a separate loopback HTTP/SFTP byte channel. `sftp_upload` issues a ten-minute session-bound +256-bit capability for Host/full path/size/SHA-256/overwrite intent; `sftp_upload_status` exposes only that +session's bounded in-memory state. PUT authenticates the capability before consuming bounded body chunks with +socket backpressure; ordinary MCP retains long-lived Bearer and its 4 MiB body cap. Capabilities appear only in +headers, never query strings, durable audit or long-lived credentials. Remote Infrastructure owns the HTTP-neutral +stream/dedicated lease, with no staging/scratch. Exact bytes plus incremental full SHA-256 authorize EOF/commit; +directories cannot be overwritten, and failed cleanup reports the temporary path. + +Interrupted uploads retain a memory checkpoint/remote partial. `upload_offset` counts acknowledged remote writes; +sequential retries require matching `Upload-Offset` and remaining-suffix Content-Length, rejecting parallel claims. +Digest includes verified prefix; committed bytes never replay. Lost HTTP responses cannot undo successful commits. +Live uploads prevent idle session expiry; retained success keeps idle sessions queryable thirty minutes. Upload +audit is metadata-only and independent of ticket-creation audit; no entitlement/schema/AgentKit change is required. + +`sftp_download` issues the reverse capability for session/Host/full path/optional expected SHA-256 with no remote +I/O at creation. GET authenticates/atomically claims before connecting; regular files only, no query credentials, +bodies or HEAD. One open-ended `Range: bytes=N-` returns 206/Content-Range; out-of-bounds returns 416 without +consuming the ticket. Clients choose N from saved bytes, not server progress. Bounded prefix reread verifies +previous bytes before headers and reconstructs digest; changed metadata/prefix rejects resume. A dedicated +`SFTPDownloadStream` worker owns handles/native pointers/queue. FSTAT fixes declared size; EOF validates exact +bytes, final size and available mtime, without promising an atomic remote snapshot. + +Download HTTP bodies pull at most 64 KiB after each previous send, never via SSE AsyncStream or staging. +SHA-256 covers transmitted bytes. Success requires source validation, completed sending, checked handle close +and lease release. Pre-header errors return safe JSON; post-header errors close without appended JSON/status +changes. Clients must poll `sftp_download_status`, verify local size/hash and finalize; 200/Content-Length alone +prove neither success nor local persistence. + +Both directions compose shared MCP metadata/activity/retention policy for waiting/paused expiry and completion pruning, +but retain separate dictionaries, live states, claims, errors, streaming/cancellation and task ordering. Upload +expiry may spawn partial cleanup; download permits valid Range after success. They share four active slots; +each keeps at most 64 unfinished tickets (paused included) and 256 terminal records for thirty minutes. +Disconnect/recoverable SSH failure pauses; explicit revocation cancels. Retry requires the original active +session/unexpired capability; process exit loses checkpoints/capabilities. Generation IDs reject old callbacks. +Session/Host retirement, server stop, token rotation and direction disablement revoke live work; release capacity +only after cleanup. Download audit is separate from MCP ticket calls and contains endpoint/sent-byte/state metadata, +never bytes/tokens. The existing upload/stdio paths remain intact. ## Callback isolation -Speech services and coordinator state are MainActor-isolated, but authorization -callbacks may arrive on any queue. A nonisolated factory creates an explicitly -Sendable continuation callback; dispatching from an actor-inherited closure would -trap before its body runs. The coordinator rechecks its recording-session token -after authorization so a late result cannot revive a cancelled session. - -iOS Core Audio taps follow the same rule: a nonisolated factory creates the realtime -tap block, which calls only Sendable buffer/level handlers. Reconnect timers and -metrics workers also enter explicit MainActor methods rather than relying on -inherited task isolation; remote I/O stays behind asynchronous/detached boundaries. +Speech state/services are MainActor-isolated; authorization callbacks may arrive anywhere. A nonisolated +factory must create an explicitly Sendable callback: actor-inherited closures can trap before dispatching. +Recheck the recording token after authorization to prevent revival. iOS Core Audio likewise creates realtime +taps in a nonisolated factory and calls only Sendable buffer/level handlers. Reconnect timers/metrics workers +enter explicit MainActor methods; remote I/O stays behind async/detached boundaries. ## Agent runtime and security -Model-provider credentials are optional on macOS and mobile. An empty credential -reference means anonymous requests; a nonempty reference whose secret is missing, -empty, or unreadable remains an error. `AIConfigurationModel.modelSecret(for:)` -shares this distinction across discovery, probes, Chat, Guardian, titles, compact, -Command Generator, and Memory review. Anonymous resolution never reads secret -storage or inherits another provider's storage error. Vertex still requires its -service account and a valid project/location. AgentKit omits empty authentication headers for ordinary providers. +### Providers and configuration -Model-provider forms show protocol-specific Name and URL hints, not prefilled input. -A shared Domain policy materializes blank/whitespace fields at the Application save -boundary: Automatic stores OpenAI's full Responses URL; explicit protocols store -OpenAI/Anthropic v1 or Google v1beta bases. Nonempty input never falls back or changes -on a type switch. Runtime reads never infer public destinations for legacy empty rows. -Desktop keeps page-owned input buffers while saving concrete defaults immediately; -mobile previews a normalized copy and normalizes again after conflict merging at Save. -Reopening shows saved values. Merely viewing or editing a form does not contact a provider. +Credentials are optional: empty references mean anonymous requests; nonempty missing/empty/unreadable secrets +are errors. `AIConfigurationModel.modelSecret(for:)` applies this to discovery, probes, Chat, Guardian, titles, +compact, Command Generator and Memory review. Anonymous resolution neither reads storage nor inherits unrelated +errors; ordinary AgentKit providers omit empty auth headers. Vertex still requires service account/project/location. -Explicit generateContent offers a Vertex AI toggle. Project, location (blank saves -as global), and service-account identity are independent of the retained ordinary -URL/API key. Leaving that type disables Vertex without deleting its configuration. -Missing Vertex credentials never fall back to an API key or anonymous access; Vertex -models are manually added. AgentKit owns address derivation and OAuth token exchange. -File/paste import validates bounded service-account JSON and trusted Google token URLs; -plaintext stays in transient drafts and AISecretStore. Two-reference credential edits -compare their original values and roll back all changed secrets on metadata failure. -Deletion removes both references; disabling Vertex removes neither. -V19 adds independent PersistentModelProviderVertexConfiguration backing without changing -V1-V18 entities. Settings sync carries only metadata/references, preserves existing -Vertex metadata when an older payload omits it, and respects explicit clears and deletes. -The existing Keychain migration covers both references; no private key enters CloudKit -configuration. Vertex data already discarded by V12 is not reconstructed. +Forms show protocol Name/URL hints, never prefilled input. Domain defaulting materializes blank fields at +Application save: Automatic stores OpenAI's complete Responses URL; explicit protocols use OpenAI/Anthropic v1 +or Google v1beta bases. Nonempty input never falls back/changes on type switch; legacy empty runtime rows never +infer public destinations. Desktop autosaves concrete values behind page buffers; mobile normalizes previews +and again after Save conflict merging. Reopen shows saved values; viewing/editing never contacts providers. -Both apps accept user-selected HTTP and HTTPS model endpoints. Their ATS arbitrary -loads exception supports unknown user-configured domains, including local servers; -HTTPS keeps default certificate trust evaluation. No local-network ATS exception -may override that setting. Local-network usage descriptions are localized. App Store -review notes must explain that users configure arbitrary self-hosted model servers, -including HTTP endpoints which the app does not own and cannot upgrade to HTTPS. +Explicit generateContent offers Vertex. Project, location (blank saves `global`) and service-account identity +are independent of retained ordinary URL/API key. Leaving the type disables Vertex without deletion; missing +Vertex credentials never fall back, and its models are manually added. AgentKit owns addresses/OAuth. Bounded +file/paste JSON validates trusted Google token URLs; plaintext stays in transient drafts/`AISecretStore`. +Two-reference edits compare originals and roll back all secret changes if metadata fails. Delete removes both; +disable removes neither. V19 adds independent `PersistentModelProviderVertexConfiguration`, freezing V1–V18. +Settings sync carries metadata/references, preserves Vertex fields omitted by older payloads, honors clears/deletes +and migrates both Keychain references; no private key enters CloudKit or reconstruction of V12-discarded data. -`AgentKit` owns the provider adapters, loop, approval gate, MCP client, and built-in -host-agnostic tools. Termind owns everything that reaches its machines or people: -SSH/SFTP tools, connection pools, Vault resolution, persistence, policy, and UI. -The additive seams are: +Both apps permit user-selected HTTP/HTTPS endpoints with ATS arbitrary loads for unknown/self-hosted domains; +HTTPS retains default trust, and no local-network ATS exception may override this. Local-network descriptions +are localized. App Store notes must explain user-owned HTTP servers the app cannot upgrade to HTTPS. -- `AgentToolCatalog.registry(builtIn:additional:)`: AgentKit groups first, then - `TermindAgentTools`; Termind and remote MCP tools cannot shadow built-ins. -- `ChatSlashCommandRegistry`: one registration owns a command's platforms, - availability, options, current-value presentation, and execution. Desktop and - mobile composers inject only host navigation, compaction, send, and access actions. -- `AgentToolServices`: typed host services, including `AgentSSHInteraction`, keep - credentials and host-key prompts outside AgentKit events and durable history. -- `TermindAgentPolicy`: Termind's prompt, plan contract, and `ssh_exec` decision; - stable prompt prefixes preserve provider caching. -- `TermindAgentPresentation`: Termind-only card values whose stable raw values keep - persisted cards decodable. +### Runtime, context, and authorization -`AgentRuntime` is an actor. `AgentContextPipeline` shapes only model requests; -stored history remains complete. `AgentTurnDriver` streams a turn, -`AgentToolScheduler` runs its batch, `AgentToolExecutor` performs schema validation, -preflight, card creation, hooks, authorization, then execution, and -`AgentEventChannel` serializes UI and durable events. Provider adapters include -OpenAI Responses/Chat Completions, Anthropic Messages, and Gemini. Models without -tool capability receive no tools; only recognized chat-purpose models appear in -conversation pickers, and unknown purpose values safely default to chat. -Ordinary planning and acting runs have no turn or tool-call count ceiling; they end -when the model finishes, the user stops, a hook terminates, or an error occurs. -Guardian remains separately bounded so unavailable or inconclusive review fails closed. -Compact, conversation titles, and Command Generator use AgentKit's buffered-result -interface over streaming transport, not `stream: false` requests. They collect -validated results before applying them and never expose intermediate fragments. -Buffered SSE responses require a terminal marker and are capped at 16 MiB of raw -response bytes; cancellation, malformed data, and premature EOF discard partial -results. Ordinary agent streaming retains its existing execution policy, and -providers returning complete JSON remain supported. -Each ordinary model turn retries only transient provider transport/service failures -five times at 1/2/4/8/16 seconds. A retry reuses the logical message and request, -abandons its partial presentation, and cannot replay a tool because tool dispatch begins -only after a complete model response. Retry progress is ephemeral; final run failures -remain durable and render as error rows rather than assistant-authored text. -AgentKit's callable `fetch` and `scratch_fetch` tools are `.ask` because each -contacts a caller-selected URL. Explicitly enabled `web_search` remains -pre-approved; a provider-native search card is a post-hoc record of a search the -selected model provider already ran. +AgentKit owns provider adapters, loop, approval gate, MCP client and host-neutral tools; Termind owns SSH/SFTP, +pools, Vault resolution, persistence, policy and UI. Additive seams: -Guardian owns Auto-reviewing as an isolated `.reviewing` agent posture. Ordinary -tools default only to `.planning` and `.acting`. The registry supports disjoint -same-name registrations with independent schema, preflight, and execution for -each run mode, but Termind currently registers no `.reviewing` tools, so every -Guardian request is tool-free. Its in-memory per-conversation channels serialize -reviews and retain a bounded full-to-delta transcript, while only direct authored -user text and locally captured `request_user_input` answers may establish authorization. -OpenAI-shaped requests use `guardian:` as the stable prompt -cache key; fixed policy, schema, and tool prefixes remain byte-identical. -Structured Guardian decisions use provider-native JSON schema when the selected -model declares support and strict local validation otherwise. Any unavailable, -timed-out, malformed, recursively gated, or policy-inconsistent result fails -closed before the pending tool executes. -An unset Guardian role is Automatic: it prefers `codex-auto-review`, then the -highest numeric version in the `gpt-*-luna`, `claude-sonnet-*`, -`gemini-*-flash`, `deepseek-v*-flash`, and `glm-*-flash` families, in that -order, before falling back to the resolved Chat model. Matching is -case-insensitive and compares numeric version components (`0.11` is newer than -`0.8`). An explicit Guardian assignment always wins. +- `AgentToolCatalog.registry(builtIn:additional:)` puts built-ins before `TermindAgentTools`; neither Termind nor + remote MCP shadows them. `AgentToolServices`, including `AgentSSHInteraction`, keeps credentials/trust prompts + outside AgentKit events and durable history. +- `ChatSlashCommandRegistry` owns command platforms, availability, options, current value and execution; + composers inject only navigation/compaction/send/access actions. +- `TermindAgentPolicy` supplies prompt, plan contract and `ssh_exec` decisions with stable cacheable prefixes; + `TermindAgentPresentation` supplies Termind cards with stable persisted raw values. -User messages carry immutable, hidden `AgentTurnContextSnapshot` metadata. Session -facts, skill catalogs, plan contracts, and surface-tool availability are captured on -submission, including steering and edits. Live surface state still belongs to the -window. Replay emits an initial baseline and then only changed blocks at their original -message boundaries; missing legacy snapshots are unknown, never reconstructed from the -current terminal. Leaving a session or mode emits an explicit clearing transition. -Retries retain their snapshot, forks copy it, and compaction retains context facts while -establishing a new baseline. V16 adds independent `PersistentAgentMessageReplay` rows; -V1-V15 message definitions remain unchanged. Deletion and truncation remove the backing. +`AgentRuntime` is an actor; `AgentContextPipeline` changes model requests only, preserving stored history. +`AgentTurnDriver` streams, `AgentToolScheduler` batches, `AgentToolExecutor` validates schema then preflights, +creates cards, runs hooks, authorizes and executes; `AgentEventChannel` serializes UI/durable events. Adapters +cover Responses/Chat Completions, Anthropic Messages and Gemini. Models lacking tool capability get no tools; +conversation pickers show recognized chat purposes and unknown purpose values default to chat. Planning/acting +have no turn/tool-count ceiling; model finish, user stop, hook termination or errors end them. Guardian is bounded +and fails closed when unavailable/inconclusive. -Tool model output is projected once before `toolFinished`, including a bounded preview -and a scratch capture reference for oversized results. Result metadata preserves the -projection across crash recovery; message backing preserves it across replay. Earlier -results are never shortened merely because another tool finishes. Paged reads retain -their structured continuation fields. `sftp_read` streams UTF-8 line windows; it and -`scratch_read` report only complete returned lines and a truthful next offset. +Compact/titles/Command Generator use buffered results over streaming, never `stream: false`, applying only +validated complete results. Buffered SSE requires a terminal marker and caps raw response at 16 MiB; cancellation, +malformed data/premature EOF discard partials. Complete JSON remains supported; normal streaming policy is unchanged. +Ordinary turns retry only transient provider transport/service failures five times at 1/2/4/8/16 seconds, reusing +the logical message/request and discarding partial presentation. Dispatch follows complete responses, so retries +cannot replay tools. Retry progress is ephemeral; final failures persist as error rows, never assistant-authored text. +Callable `fetch`/`scratch_fetch` are Ask (`.ask`) for caller-selected URLs; enabled `web_search` is pre-approved and native +provider search cards record searches already performed. -Terminal suggestion definitions remain stable across surfaces. The executor checks -availability from the most recently delivered user snapshot before preflight; merely -changing focus does not change the active run's capabilities. Termind's stable system -prompt explains that surface availability is a partial override, not a tool catalog; -terminal suggestion availability does not restrict independent SSH/SFTP tools. -The standalone Assistant need not attach session context: no active session means -no default target, not an inability to execute. Discover targets with `list_hosts` -and ask only if the user's intended target remains ambiguous; no sidebar or -terminal connection is a prerequisite. -SSH/SFTP login is handled by the host configuration and app authentication UI. -`request_user_secret` is only for command secret input, not a prerequisite for SSH; -`ssh_exec` needs only `host_id` and `command` for ordinary commands. A terminal -suggestion creates a user-operated card and never constitutes command execution. -Provider tool/schema JSON is deterministic. Responses and Chat Completions requests use a conversation cache key -without hostname restrictions; Anthropic uses -short-lived system/message cache breakpoints, and Gemini retains implicit caching. -Cache read/write usage is normalized without adding cached tokens twice to input totals. +Guardian is isolated `.reviewing`; ordinary tools default to `.planning`/`.acting`. The registry permits disjoint +same-name mode registrations with separate schemas/preflight/execution, but Termind registers no reviewing tools, +so Guardian is tool-free. Bounded full-to-delta in-memory channels serialize each conversation's reviews. Only direct +authored user text/local `request_user_input` answers authorize. OpenAI-shaped requests use `guardian:` +cache keys and byte-stable policy/schema/tool prefixes. Declared support uses native JSON schema; otherwise validate +strictly locally. Unavailable/timed-out/malformed/recursive/policy-inconsistent decisions fail closed before execution. +Automatic Guardian prefers `codex-auto-review`, then highest numeric versions in `gpt-*-luna`, `claude-sonnet-*`, +`gemini-*-flash`, `deepseek-v*-flash`, `glm-*-flash` order, then resolved Chat. Matching ignores case and compares +numeric components (`0.11` > `0.8`); explicit assignment wins. -A batch is concurrent only if every call is parallel. Preflight may tighten this: -`ssh_exec` is parallel only when local command classification proves it safe; an MCP -destructive hint makes a call sequential. Results always enter the -transcript in model order, and every call gets an answer even after cancellation or -refusal. Approvals/questions are queued one at a time; state derives from outstanding -work. Authorization is a structural executor stage and cannot be removed by hooks. +Immutable hidden `AgentTurnContextSnapshot` captures session facts, skills, plan contract and surface-tool +availability at each submission/steer/edit; live surface state remains window-owned. Replay emits baseline then +changed blocks at original boundaries, treating absent legacy snapshots as unknown, never current terminal state. +Leaving session/mode explicitly clears it. Retry retains, fork copies, and compaction preserves facts with a new +baseline. V16 adds independent `PersistentAgentMessageReplay`; V1–V15 messages stay frozen, with backing removed +on deletion/truncation. -Command classification recursively unwraps supported shell wrappers and static nested -commands, then applies argument- and subcommand-aware rules. Only explicitly proven -read forms are locally read-only; unknown tools, options, embedded programs, excessive -complexity, and unsupported shell syntax fail closed to a state-changing assessment. +Project tool output once before `toolFinished`: bounded preview plus scratch reference for oversized data. +Result metadata retains projection for crash recovery and message backing for replay; later completions never +shorten earlier results. Paged reads retain continuation fields; `sftp_read` streams UTF-8 line windows and, +like `scratch_read`, reports complete lines and truthful next offsets. -Every call has an approval policy before the run's permission mode is consulted: +Terminal suggestions have stable definitions across surfaces; executor availability uses the latest delivered +user snapshot before preflight, never focus changes. Surface availability is a partial override, not a catalog +or restriction on independent SSH/SFTP. Standalone Assistant has no default target without a session; `list_hosts` +discovers targets and questions are needed only for remaining ambiguity, never to require sidebar/terminal attachment. +App configuration/UI handles SSH login; `request_user_secret` is for command secrets, not SSH prerequisites. +Ordinary `ssh_exec` needs only `host_id`/`command`; suggestions create user-operated cards, never execution. +Provider schemas are deterministic. Responses/Chat use conversation cache keys without hostname restriction, +Anthropic short-lived system/message breakpoints, Gemini implicit caching. Usage never double-counts cached input. -- Deny refuses every permission mode while keeping the tool registered and visible - to the model; Approve runs without another approval step. -- Ask for approval sends Ask calls to the user. Approve for me sends them to Security - Review; missing credentials, - timeout, transport failure, or malformed review fails closed and is recorded. -- Full access pre-approves Ask calls but retains host-key, secret, schema, - path, fetch, sandbox, and capability boundaries. -- A genuinely new MCP tool starts at Approve only when its remote - `readOnlyHint` is true and its `destructiveHint` is not true; every other new - tool starts at Ask. Rediscovery preserves the user's stored policy, including - an explicit Approve choice for a destructive tool. -- MCP tools follow Deny, Ask, or Approve in both acting and planning runs. +A batch runs concurrently only if every call is parallel. Preflight can tighten: `ssh_exec` needs locally proven +safe classification, MCP destructive hints force sequential. Results stay model-ordered and every call gets an +answer after cancellation/refusal. Approvals/questions queue singly; state derives from outstanding work. Hooks +cannot remove structural authorization. Command classification recursively unwraps supported wrappers/static +nested commands with argument/subcommand rules; unknown tools/options/programs, excess complexity or unsupported +syntax fail closed to state-changing. -Skills are global, app-managed packages. Settings imports and approved `skill_manage` -transactions use the same user library and Settings sync journal. Skill Creator and -Skill Installer are immutable application instructions merged into runtime snapshots; -they appear in the catalog, `/skill`, and read tools, but never in Settings, persistence, -or sync. Their reserved names cannot be shadowed or mutated. Existing user collisions -remain visible and paused until renamed. There is no starter seeding or Skills Reset. -Previously installed starter skills remain ordinary user-owned records, including -unchanged or disabled copies. Their identities, contents, resources, and sync behavior -are preserved without migration or special recognition; new installations start with -an empty user library. The old starter definitions and fixtures are not shipped. +Each tool has policy before permission mode: -`skill_install` is acting-only and uses the existing Ask/Guardian/Full Access boundary. -AgentKit resolves public GitHub skill directories to one commit, bounds and verifies -all downloaded bytes, and rejects redirects, symlinks, submodules and incomplete packages. -A run-owned in-memory staging area binds reviewed source, files and installation name -to the invocation; execution commits only those bytes against the reviewed library revision. -Termind alone persists the complete package and Settings sync intent in one transaction. -Installation executes no scripts, never overwrites an existing name, and becomes -available on the next run. `skills_list` snapshots -inventory, and `skill_read_file` reads bounded SKILL.md instructions or paged supporting -resources. Only SKILL.md is an instructional result; supporting files remain reference -data and no frontmatter grants permission. Mutations are acting-only, Ask, atomic, and -bound to the library revision reviewed at preflight. Running agents retain their starting -package snapshots; edits become available on the next run. The retired `load_skill` -name has historical presentation/replay support only. V18 adds independent package -backing while V1-V17 models remain frozen. Complete packages sync with existing skills; -legacy records without package fields cannot erase supporting files. +- Deny refuses in all modes while remaining model-visible; Approve needs no further approval. +- Ask for approval sends Ask to users; Approve for me sends it to Security Review. Missing credentials, timeout, + transport failure or malformed review fail closed and are recorded. +- Full access pre-approves Ask but retains host-key/secret/schema/path/fetch/sandbox/capability boundaries. +- New MCP tools default Approve only with `readOnlyHint == true` and `destructiveHint != true`; otherwise Ask. + Rediscovery preserves stored policy, even explicit Approve for destructive tools. MCP obeys policy in acting/planning. -Hermes Memory (持久化记忆 / 持久化記憶 in Chinese) is a device-local, default-off feature. AgentKit owns generic memory -values, content screening, tools, learning, and revocable prompt snapshots; Application -owns lifecycle and UI state; Persistence alone imports GRDB. Its dedicated Application -Support database stores bounded global `memory` (2200 Unicode scalars) and `user` -(1375 scalars), frozen per-conversation snapshots, learning checkpoints, and the FTS5 -history index. SwiftData remains the original conversation source. Forks retain the -snapshot reference, not a second copy of memory in SwiftData. Tool writes are atomic; -background reviews compare their base state transactionally before committing. Skills -always retain their separate approval boundary. Memory and credentials never sync. +### Skills, memory, and durable data -Disabling memory revokes live access and prompt snapshots and cancels learning/indexing, -but preserves storage. Destroy is user-only: persist disablement and an unfinished-destroy -marker, revoke all leases, cancel and join work, close GRDB, then remove its entire owned -directory. Failed destruction remains disabled and is retried at launch. No stale task -can write through an old generation. Skills, original messages/tool results, and exported -files survive. Explicit re-enablement creates a new memory store and permits full history -backfill; old chats may therefore teach the agent the same facts again. Disabled startup -does not create or open a database. Planning can search history but cannot write memory. +Skills are global app-managed packages; Settings imports/approved `skill_manage` use the same library/journal. +Immutable application Skill Creator/Installer merge into runtime snapshots, catalog, `/skill` and reads, never +Settings/persistence/sync. Reserved names cannot be shadowed/mutated; existing collisions stay visible and paused +until renamed. No starter seeding/Reset or shipped starter definitions/fixtures. Existing starters remain ordinary +user records, preserving disabled/unchanged content, resources, identities and sync; new libraries start empty. -Tools never receive the interactive PTY. SSH exec uses a dedicated channel and file -operations use run-scoped SFTP. User-entered secrets are single-use handles bound to -run/tool/host/purpose; plaintext never enters model input, reviews, events, logs, or -SwiftData. A live approval is the deliberate user-review boundary: while it is pending, -the local UI projects the invocation's original arguments through the registered tool -presenter so the person can inspect exactly what will run. That projection is neither -recorded nor reused after the decision. Fetch permits proven public HTTP(S) reads, revalidates DNS on every -redirect, reauthorizes entry into a restricted origin, follows explicit headers like -`curl -H -L`, and redacts sensitive values. Remote MCP metadata can tighten but never -relax local safety. +Acting-only `skill_install` uses Ask/Guardian/Full access. AgentKit pins public GitHub directories to one commit, +bounds/verifies all bytes and rejects redirects/symlinks/submodules/incomplete packages. Run-owned memory staging +binds reviewed source/files/name to invocation/library revision; Termind atomically persists that package plus +Settings intent. No scripts/overwrite; availability starts next run. `skills_list` snapshots inventory; +`skill_read_file` provides bounded SKILL.md or paged reference resources. Only SKILL.md is instructional; supporting +files/frontmatter grant no permission. All mutations are acting-only/Ask/atomic/revision-bound; running snapshots +remain fixed. `load_skill` is historical presentation/replay only. V18 adds package backing while freezing V1–V17; +legacy sync records cannot erase supporting files. -Each conversation has a bounded `AgentScratchWorkspace` under app-owned Application -Support. It rejects absolute escape, `..`, and symlinks, proves resolved containment, -and serializes mutations per path. Pull/push move remote bytes without putting them -in the transcript; these host-touching endpoints remain Termind-owned. Edit returns a -diff and search bounded previews. Push approval is bound to size/digest and includes -a redacted head, so changed content is refused. +Hermes Memory (持久化记忆 / 持久化記憶) is device-local/default-off. AgentKit owns values, screening, tools, learning and +revocable prompt snapshots; Application owns lifecycle/UI; only Persistence imports GRDB. Its dedicated Application +Support database stores global `memory` (2,200 Unicode scalars), `user` (1,375), frozen conversation snapshots, +learning checkpoints and FTS5 history. SwiftData stays the original conversation source; forks reference snapshots +without copying memory into SwiftData. Tool writes are atomic; background reviews transactionally compare base +state. Skills retain separate approval; memory/credentials never sync. Planning can search, never write memory. -Agent conversations, runs, visible messages, tool calls/results, and reviews are -stored after truncation/redaction. Launch marks unfinished runs interrupted and never -replays remote actions. Restore materializes completed results missing from the last -snapshot; remaining unanswered calls receive an explicit unknown-outcome repair so -the model cannot assume an action was never dispatched. +Disable revokes access/snapshots and cancels learning/indexing but preserves storage. User-only Destroy persists +disablement/unfinished marker, revokes leases, cancels/joins work, closes GRDB and removes its owned directory. +Failure stays disabled/retries at launch; generations reject stale writes. Skills, original messages/results and +exports survive. Explicit enable creates a new store and allows full backfill, so old chats may reteach facts. +Disabled startup neither creates nor opens the database. + +Tools never get interactive PTYs: exec uses dedicated channels and file tools run-scoped SFTP. User secrets are +single-use handles bound to run/tool/Host/purpose; plaintext never reaches model/review/events/logs/SwiftData. +Pending approval alone projects original arguments via the registered presenter for local inspection; never +record/reuse that projection after decision. Fetch allows proven public HTTP(S), revalidates DNS per redirect, +reauthorizes restricted-origin entry, follows explicit headers like `curl -H -L` and redacts sensitive values. +Remote MCP metadata may only tighten safety. + +Each conversation owns a bounded Application Support `AgentScratchWorkspace` rejecting absolute escape, `..` +and symlinks, proving containment and serializing per-path mutation. Termind pull/push moves remote bytes outside +transcripts; edits return diffs, searches bounded previews. Push approval binds size/digest/redacted head and +rejects changed content. Conversations/runs/messages/tool calls/results/reviews persist after redaction/truncation. +Launch marks unfinished runs interrupted, never replaying actions. Restore materializes missing completed results; +unanswered calls get explicit unknown-outcome repair, never an assumption they were undispatched. + +## Assistant presentation + +Presentation owns tool summaries, batch status, bounded previews, timestamps, Markdown sanitization/soft breaks +and disclosure/deadline state. Hosts inject resolved `AgentToolDetailPresenter` from Remote Infrastructure; +Presentation never imports its transport registry. Protocol results/persisted schemas remain unchanged. + +ListViewKit stays app-owned/allowlisted: macOS transcript, large SFTP lists, conversation library and tool sidebars; +iOS transcript/large SFTP lists through UIKit. Bounded navigation/archive pages use SwiftUI `List`. TFM retains +AppKit rows, Markdown caches and edit state; TFI hosts SwiftUI within its UIKit bridge. Presentation-only revisions +explicitly reconfigure mobile rows, invalidate only that height and retain `UIHostingConfiguration` content views. +Display/measurement purpose, Reduce Motion and scene activity cross the host boundary. Only displayed active +content starts timers/scanning; shared scan parameters/three-second reasoning deadlines never restart on reuse. +Work completion follows the shared default only after the entire row leaves the viewport, unless disclosure was +chosen by the reader. Desktop width changes reconfigure mounted rows/constraints/Markdown geometry without +replacing cached lists or following the tail; parsed Markdown survives width-dependent measurement changes. + +Direct approval callbacks idempotently stage cards before responses. Queued proposed/approval events update the +same identity without erasing denial, regardless of MainActor arrival order. AgentKit owns UI-neutral skill/memory +result/approval projections. Presentation repairs historical summaries only for recognized local tool/presenter +identities, never MCP/unknown presenters matched by name. Compact previews share 600 characters/five rows across +the card and a scaled 180-point viewport. Window-owned full expansion survives reuse; Copy keeps the full sanitized +projection. Long approvals retain all live arguments in a scaled 240-point scroll region with actions outside. +Neither path reads live skill/memory stores or changes protocol results, authorization or persisted data. ## Pro access and review invitations -`ProAccessController` owns application-wide StoreKit state. Permanent Pro is a -Family Sharing non-consumable; annual Pro is a Family Sharing auto-renewable -subscription whose introductory-offer eligibility, free period, billing period, -localized price, expiration, and renewal state come from StoreKit. Permanent access -wins over subscription access. Subscribed, introductory-trial, and -verified grace-period states grant Pro; billing retry without grace, expiration, -refund, and revocation do not. Product metadata failure never replaces verified -access. `ProOfferPresentation` derives purchase labels, disclosures, and period text -from value inputs, never from a StoreKit controller. Native paywalls retain purchase -and navigation state. macOS supplies explicit app-language lookup; mobile retains -Apple's bundle lookup, independently of the locale used for duration formatting. +App-wide `ProAccessController` owns StoreKit. Permanent Pro is Family Sharing non-consumable; annual Pro is a +Family Sharing auto-renewable subscription. StoreKit supplies eligibility, free/billing periods, localized price, +expiration and renewal. Permanent wins; subscribed/trial/verified grace grants access, billing retry without grace/ +expiration/refund/revocation does not. Metadata failure never replaces verified access; retired standalone-trial +transactions are ignored. Recheck expiration on activation and at most minutely while active. +`ProOfferPresentation` derives labels/disclosures/periods from values, never controllers; native paywalls own +purchase/navigation. macOS injects app-language lookup, mobile Apple's bundle lookup, independent of duration locale. -The retired standalone trial product and its historical transactions are not recognized. -Entitlement expiration is checked on activation and at most minutely while active. -Workspace composition is free on macOS, including splitting, merging, rearranging, -duplicating, broadcasting input, batch opening, multi-host snippets, and local templates. -It never depends on StoreKit or retains a launch-scoped Pro grant. Template iCloud sync -and all other Pro features use the current entitlement immediately. +Desktop Workspace composition is free: splits, merge/reorder/duplicate, broadcast, batch open, multi-host snippets +and local templates never depend on StoreKit or sticky launch grants. Template iCloud sync/other Pro features use +current entitlement immediately. -One app-wide shared `AppReviewController` stores device-local first-use/active-day/version/ -attempt history, never syncs it, and never claims a review was shown. Desktop windows -and mobile scenes use the same surface-based policy; their platform adapters alone own -native focus, input, pointer, lifecycle, and modal observation. Automatic -requests require seven elapsed days, three active dates, and five foreground -workspace minutes, then a three-second quiet transition to a Vault list or Settings -index/App page. Input, navigation, focus loss, sleep, or modal presentation cancels -the attempt. Attempts are persisted before StoreKit, limited to once per marketing -version and a 120-day cooldown; manual review restarts the cooldown. Tests and -screenshot launches disable live requests. +One app-wide `AppReviewController` stores device-local first-use/active-days/version/attempts, never syncs or claims +display. Windows/scenes share surface policy; apps own native focus/input/pointer/lifecycle/modal observation. +Automatic review requires seven elapsed days, three active dates, five foreground Workspace minutes, then three +quiet seconds entering a Vault list or Settings index/App. Input/navigation/focus loss/sleep/modal cancels. +Persist attempts before StoreKit; once per marketing version and 120-day cooldown, restarted by manual review. +Tests/screenshot launches disable live requests. ## Server metrics -`ServerMetricsModel` is application-owned and shares one serial SSH sampler per saved -host across windows, menu bar, and mobile. Rows exist before secrets/networking and -represent connecting, collecting, live, paused, sleeping, or actionable failure. -Workers run detached and enter short, synchronous MainActor-isolated state steps; no -long-lived async method owns observable or host-store state. Credential lookup and remote -I/O stay off actor, and cancellation is rechecked after lookup. Secrets are cleared from -memory on pause/removal/termination. Known-host and credential decisions remain interactive -and scoped to the active monitor. +App-owned `ServerMetricsModel` shares one serial SSH sampler per saved Host across windows/menu bar/mobile. +Rows precede secrets/network and represent connecting/collecting/live/paused/sleeping/actionable failure. +Detached workers enter short synchronous MainActor state steps, never long-lived observable/host-store async +methods. Credential lookup/I/O stays off actor; recheck cancellation after lookup and clear secrets on pause/ +removal/termination. Trust/credential interaction belongs to the active monitor. Screenshot launches inject frozen +in-memory metrics into the production WindowGroup with restoration off: no saved config, samplers or discovery. -Screenshot launches inject a frozen, in-memory metrics model into the production -WindowGroup with restoration disabled. It never reads saved monitor configuration, -starts samplers, or performs host-key discovery. +Infrastructure reads bounded Linux procfs/optional hardened `nvidia-smi` XML. Base data survives GPU/process/filesystem +failure; invalid deltas are unknown. Rates use remote uptime, identity PID+start time, RSS remote page size; collect +no arguments/environment. Five-minute samples stay in memory; order/pause/device selection persists. Details alone +enable process/filesystem collection and capacity refresh. Default network aggregation prefers physical interfaces; +explicit selection overrides it. -Infrastructure reads bounded Linux procfs and optional hardened `nvidia-smi` XML. -Base metrics survive GPU/process/filesystem failure; invalid deltas remain unknown. -Rates use remote uptime, process identity uses PID plus start time, RSS uses remote -page size, and no process arguments/environment are collected. Five-minute samples -are in memory; ordered monitors, pause, and device selection persist. Filesystem -capacity refreshes only while details are observed. Network defaults prefer physical -interfaces, with explicit selection overriding aggregation. +Read-only/parallel/planning-safe `get_metrics` exposes existing Sendable snapshots to Agent/authenticated MCP, +validating Host IDs, preserving section time/age/staleness, using null for unknown/nonfinite values and limiting +processes to 20. It cannot add/sample/observe details/read auth; monitoring itself does not depend on AgentKit. -`get_metrics` exposes an existing monitor's Sendable snapshot to Agent and the -authenticated MCP bridge. It cannot add hosts, sample, observe details, or access -auth state. It is read-only, parallel, planning-safe, validates host IDs, preserves -section timestamps/age/staleness, uses null for unknown/non-finite values, and limits -processes to 20. Monitoring itself does not depend on AgentKit. +Metrics order syncs separately from Vault; layout/sort/search/detail navigation/menu visibility do not. Authentication +uses shared Journey while the coordinator owns transport. Each monitor supplies its own keyboard-interactive queue, +including jumps, never app sheet/external-window fallback. Cards signal auth without navigating; opening presents +Journey. Leaving details retains challenges; explicit cancellation pauses and returns that window to the list. +Retirement cancels queued/not-yet-enqueued responders; generation/request IDs isolate answers/cleanup. Temporary trust +clears on pause/removal/config change; rejection pauses. Free access atomically permits three cards including paused, +never deleting restored cards on downgrade; Pro/trial is unlimited. -Metrics order syncs independently of Vault order; local layout, process sorting, -search, detail navigation, and menu-bar visibility do not. Detail observation alone -enables process/filesystem collection. Authentication opens the shared Connection -Journey while the metrics coordinator retains transport ownership. Each monitor owns -its keyboard-interactive queue, including challenges from its jump hosts. Metrics -always supplies its own responder, never falling back to an app-owned sheet or external -authentication window. Cards indicate required authentication without changing navigation; -opening a card presents its Journey. Leaving details retains the challenge, while explicit -authentication cancellation pauses the monitor and returns that window to the list. -Retiring an attempt cancels both queued and not-yet-enqueued responders; generation and -request IDs prevent stale answers or cleanup from affecting another attempt. Temporary trust -is cleared on pause/removal/configuration change; rejection pauses the monitor. -Free access admits at most three cards (paused included), atomically and without -deleting restored cards after downgrade; Pro/trial access is unlimited. - -The macOS status item/popover is process-owned and receives its model explicitly. -Closing the last window keeps the process alive while the menu item or monitors need -it; Quit cancels monitoring. System sleep and an inactive login session independently -suspend monitoring; collection resumes only after the system is awake and the session -is active, when the Data Protection Keychain is available again. Native lifecycle, -window reopening, input modality, and screenshot fixture wiring remain in `TFM/Platform`. +macOS process-owned status item/popover receives the model explicitly. Last-window close keeps the app alive for +menu/monitors; Quit cancels. System sleep and inactive login independently suspend; resume only awake/active when +Data Protection Keychain is available. Native lifecycle, reopening, input and screenshot wiring stay in `TFM/Platform`. ## Settings replication -iCloud has one master switch plus independent Vault, Metrics, and Settings choices. -Vault/Metrics default on, Settings off; disabling the master preserves selections, -and upgrades do not re-enable it. Pro gates three independent coordinators: -`TermindVaultV1`, `TermindMetricsV1`, and the compatibility-named -`TermindAIConfigurationV1`. +Master iCloud plus independent Vault/Metrics/Settings choices default on/on/off; disabling master preserves choices +and upgrades never re-enable. Pro gates independent `TermindVaultV1`, `TermindMetricsV1`, compatibility-named +`TermindAIConfigurationV1` coordinators. Each composes its own Infrastructure `CloudSyncLifecycle` for serialized +operations/generations, consent, engine creation, fetch-before-send, pause/retry, resets and status precedence. +Value-projected journal adapters preserve Persistence-only model/schema ownership. Zones/subscriptions/switches/ +account bindings/journals/engines remain independent; categories own coding/quarantine/merge/version acknowledgements. +Settings authorizes secret upload only after successful current-engine fetch and revokes on pause/account retirement. +Old operations cannot clear replacements or publish late results; the internal transport seam enables offline +deterministic lifecycle tests. -Each coordinator composes its own Infrastructure `CloudSyncLifecycle`. The driver -owns serialized operations and generation checks, account consent, engine creation, -fetch-before-send readiness, pause/retry, reset orchestration, and status precedence. -Typed category adapters project journal state as values; persistent model types and -schemas remain exclusively in Persistence. Zones, subscriptions, switches, account -bindings, journals, and engine instances remain independent. Category coordinators -retain record coding, quarantine, merge rules, and versioned acknowledgements. -Settings authorizes secret uploads only after a successful current-engine fetch and -revokes authorization on pause or account retirement. Retired operations cannot clear -a replacement operation or publish a late completion/error. The transport seam is -internal and permits deterministic lifecycle tests without contacting iCloud. +Frozen V13 Settings shapes remain; V14 adds `PersistentTerminalPreferences`, V15 independent Metrics journal/account/quarantine, +V21 `PersistentSFTPPreferences`. Settings commits data/upload intent atomically. Separate records cover providers/models, +MCP clients, skills, roles, Web Search, terminal/custom text and SFTP limits. First SFTP row imports existing defaults +(including 3/2), missing values 15/5; preparation adds missing baseline even to bootstrapped stores. Later preparation +uses persisted truth, never reimports mirrors. `v1/sftp/default` uses distinct `SFTPPreferencesItem` in the same zone; +older clients ignore its type before decoding. Other settings retain `AIConfigurationItem`; both keep V1 envelopes. +Invalid limits/type mismatches quarantine at read. Local saves commit intent then mirror defaults; remote application +refreshes models without local echo. Last-used conversation model stays local; active Agent snapshots remain fixed. -V13 Settings entity names/defaults are frozen; V14 only adds -`PersistentTerminalPreferences`; V15 adds the independent Metrics journal/account/ -quarantine. Settings persistence atomically commits data and upload intent. Separate -records cover providers/models, MCP clients, skills, roles, Web Search, terminal -preferences/custom text, and the two SFTP concurrency limits. V21 adds -`PersistentSFTPPreferences` without changing historical schemas. Its first row imports -existing UserDefaults limits (including 3/2), or 15/5 for missing values, and preparation -adds the missing baseline record even for previously bootstrapped stores. Subsequent -preparation uses persisted truth, never reimports the defaults mirror. -SFTP uses one `v1/sftp/default` record in the existing Settings zone and a distinct -`SFTPPreferencesItem` CloudKit type. Older clients ignore that type before decoding; -existing settings keep `AIConfigurationItem`. Both record types use the unchanged V1 -envelope. Invalid SFTP limits and mismatched record types are quarantined at the -read boundary. Local changes commit preferences and upload intent together, then -mirror the legacy defaults keys; remote application never generates a local echo. The most recently selected conversation model remains -device-local. Remote commits refresh models without -re-entering local mutation. Active Agent runs keep their starting snapshot. -Every model role has a live synced record: a nil Command Generator assignment means -Same as Chat and a nil Guardian assignment means Automatic, while Chat's explicit -selection is synced normally. Resolved fallback models remain local. The legacy -`securityReview` record ID is accepted only at the CloudKit read boundary, migrated -to `guardian` without reusing its record system fields, and all new writes use the -canonical ID. +Roles always have live synced records: nil Command Generator = Same as Chat, nil Guardian = Automatic; explicit Chat +syncs, resolved fallbacks do not. Accept legacy `securityReview` only at CloudKit read, migrate to `guardian` without +reusing system fields, write canonical IDs only. Conversations, approval mode, local MCP listener config and unrelated +defaults never sync. Listener switches/port/tools use defaults, token this-device-only Data Protection Keychain. +Provider/MCP secrets use `AISecretStore`; migration requires verified account/cloud copy/durable local marker. Reads +never cache; deletion markers prevent late Keychain resurrection. Custom MCP headers/terminal text sync as ordinary +settings; explicit credentials never enter CloudKit. -Conversations, approval mode, local MCP listener settings, and unrelated defaults do -not sync. Listener enablement, port, and tool selection remain UserDefaults; its token -uses a this-device-only Data Protection Keychain. Explicit provider/MCP credentials -use `AISecretStore` and migrate only after account verification, verified cloud copy, -and a durable local marker. Reads do not cache secrets; deletion markers prevent late -Keychain delivery from reviving cleared values. Custom MCP headers and terminal text -are ordinary synchronized settings, but explicit credentials never enter CloudKit. - -Journals retain tombstones and unresolved references across partial fetches. Baseline -defaults yield to cloud records. Application skills stay outside synchronized records; previously installed starters -remain user-owned, and user deletions survive migration. Duplicate skill names/MCP namespaces retain data, but -only the lowest UUID executes until resolved. Metrics keeps one versioned record per -host; records whose Vault host is absent remain hidden, and samples, prompts, secrets, -and UI preferences never enter its zone. +Journals retain tombstones/unresolved references through partial fetches; cloud beats baseline defaults. Application +skills stay excluded; user-owned starters/deletions survive migration. Duplicate skill names/MCP namespaces retain +all data but only lowest UUID executes. Metrics keeps one versioned record/Host, hides records without Vault Hosts, +and excludes samples/prompts/secrets/UI preferences. ## Mobile boundaries -Each mobile window owns one native sidebar-adaptable TabView with five stable -feature stacks, ordered Vault, Metrics, Workspaces, Assistant, Settings. The system -adapts the tab bar and sidebar without replacing the root navigation tree. Width -changes retain the selected feature, child pages, searches, scroll state, sheets and -unsaved forms. Metrics has one independent stack in all widths; Vault search shortcuts -select that destination rather than constructing another Metrics page. Resizing never -restarts live connections, transfers or Agent runs, or creates a new conversation. +### Adaptive navigation and native surfaces -Mobile toolbar policy reads actual tab-bar placement and the system's preferred vertical -toolbar edge inside each stable feature stack. Feature views consume this one policy -instead of independently reading placement or device capabilities. Workspace details -retain the five application tabs beside top bars and in vertical toolbars. Bottom tabs -stay hidden in SSH/SFTP details whenever the vertical toolbar is unavailable. -Workspace and Assistant share a `rectangle.stack` switcher in top and vertical bars, -with a title menu elsewhere. When Assistant has no pinned or active conversations, -its icon switcher stays visible but disabled, and its title has no menu. A sole current -conversation still enables switching; archived conversations do not. -These switchers select peers within the feature; they are separate -from the five application tabs. Assistant library pages always retain their titles. -Metrics uses a labeled icon menu in a vertical toolbar and a principal segmented picker -in a horizontal toolbar, hiding either when both panels are visible. iOS 27 uses the -system ToolbarOverflowMenu for secondary actions; older systems keep labeled menus. -Save stays a standard labeled button while a form-level progress indicator shows work. -SSH/SFTP search retains the minimized system search button in all toolbar placements; -opening a sidebar or changing pose never activates search or takes keyboard focus. +Each window owns one sidebar-adaptable TabView with five stable stacks: Vault, Metrics, Workspaces, Assistant, +Settings. System adaptation never replaces the root tree; resizing preserves selection/children/search/scroll/ +sheets/drafts/connections/transfers/runs/conversations. Metrics has one independent stack; Vault shortcuts select it. +`MobileFeatureRegistry` injects stable IDs/labels/symbols/factories and one tab/sidebar order; duplicate IDs fail +immediately, with no global singleton or separate shell switches. -Mobile content reads active division and occlusion ReservedRegions on iOS 27.1 after -system safe-area and keyboard layout. Frames already include margins and use the same -leading-origin coordinate convention as SwiftUI Layout, including RTL. A terminal and -its shortcut strip and notices occupy the largest continuous unobstructed rectangle, -with ties resolved toward the top and then leading edge. Ghostty remains the grid-size -authority and resizes the existing PTY; invalid viewport updates are ignored. Workspace -native surfaces and transports remain owned by their existing session, never by pose. -Assistant keeps one native transcript and one scrollable interaction surface mounted; -active folds place them above/below or leading/trailing, with the complete interaction -surface (including tasks, approvals and questions) inside its region. With one remaining -region it returns to the stacked layout. Metrics keeps both panels mounted at their last -valid sizes, preserving native lists, sort and scroll state. An active fold assigns one -panel to each usable region; with only one region it shows the selected page. Without a -fold it retains the 700-point two-column threshold. Region policy and native identity -tests exercise folding without changing navigation, drafts, focus or session ownership. +Each feature stack derives toolbar policy from actual tab-bar placement/system preferred vertical edge; features +consume that single policy, never device capabilities. Workspace detail retains app tabs beside top/vertical bars; +bottom tabs hide in SSH/SFTP when vertical toolbar is unavailable. Workspace/Assistant use `rectangle.stack` peer +switchers at top/vertical bars, title menus elsewhere, distinct from app tabs. Assistant icon remains disabled with +no pinned/active chats; its title has no menu. One current chat enables switching, archives do not; library titles +remain. Metrics uses vertical labeled-icon menu or horizontal principal segmented picker, hiding both when panels +coexist. iOS 27 uses `ToolbarOverflowMenu`, older OS labeled menus. Save stays labeled with form progress. Native +minimized SSH/SFTP search stays in every placement; sidebar/pose changes never activate search or claim focus. -Editor saves do not gate layout changes. Their tasks, duplicate-submit protection, -unsaved-dismiss confirmation and error feedback survive width changes. Sheets follow -the presenting window's width using stable presentation modifiers. Cards use container -width and one column at accessibility text sizes; SFTP breadcrumbs fit three or two -trailing components to the available space. Device idiom is not a production layout -input. Navigation and draft states remain memory-only and independent per window. +On iOS 27.1 content reads active division/occlusion ReservedRegions after safe-area/keyboard layout. Frames include +margins and follow SwiftUI's leading-origin convention, including RTL. Terminal/shortcuts/notices occupy the largest +continuous unobstructed rectangle, ties top then leading. Ghostty resizes the existing PTY and rejects invalid +viewports; session ownership survives pose. Assistant keeps one transcript and one scrollable interaction surface +mounted, including tasks/approvals/questions, placing them across fold regions or stacking with one region. Metrics +keeps both panels mounted at last valid sizes with list/sort/scroll identity, one per usable fold region or selected +page with one; without folds use the 700-point threshold. Region/native-identity tests preserve navigation, drafts, +focus and session ownership. -Neither layout shows Welcome. Each Workspace owns exactly one SSH or SFTP session; mobile never composes -panes, even with Pro. Assistant is independent. The title menu switches open peers -without reconnecting existing sessions; selecting a Recent Workspace creates and connects it on demand. -More owns Rename/Close and cross-opening SSH/SFTP when a live path exists. Explicit close never retries. +Save tasks, duplicate-submit prevention, dirty-dismiss confirmation and errors survive layout changes without +gating them. Stable sheet modifiers track window width. Cards use container width/one accessibility column; +SFTP breadcrumbs fit three or two trailing components. Device idiom never drives production layout. Navigation/ +drafts stay memory-only and window-independent. Application derives Host/Snippet sections once; Presentation owns +History subtitles, Vault sync/transfer labels and exported SFTP drag type. Running labels distinguish upload/download, +completed labels upload/download/copy; other states use Domain labels. Apps retain native composition/selection/menus/ +clipboard. Host Picker/Snippet operation state belongs to mobile Application; descriptors use Domain +`ConnectionHistoryEntry.Kind`, samplers enter via `MobileMetricsServices`, and both apps use Remote Infrastructure +`AgentHostServices` for host resolution/auth/connections/background commands. -Initial connection failure and recovery stay in Connection Journey. Background-task -expiration closes transports but retains intent; foreground resumes existing -workspaces. Mobile saves the full ordered list of device-local Workspace descriptors -(stable ID, Host ID, kind, title and duplicate index) under `mobile.workspaces.v1`. -Cold launch loads descriptors into Recent Workspaces after loading Hosts, leaving -`workspaces` empty: no sessions, connections or history events are created. One -MainActor open-by-ID entry point reuses an existing instance or registers and connects -only the requested descriptor, preserving its ID and recording history once across -scenes. Failures require explicit retry. New and restored instances share the same -ordered saved list; saving current instances alone must never discard unopened entries. -Commands, credentials, paths, terminal contents and transfers are excluded. +### Workspaces, restoration, and activity -The store reads both legacy bare arrays and the version-1 object envelope. Legacy -records may lack duplicate indices; migration assigns the lowest free index per Host -and kind in array order, reserving explicit indices, and immediately writes the envelope, -including for an empty array. Successful loads permit later saves. Missing Hosts are -pruned; unknown versions and corrupt snapshots retain their bytes and block writes -until a successful load. Close, normal SSH exit and local or remote Host deletion -remove current and unopened saved entries. Close All also clears Recent Workspaces, -while retaining connection history. Recent Workspaces is a separate list from connection -history, whose actions still create new sessions. The history section appears only when -both current and Recent Workspaces are empty. Titles reserve indices across current -and unopened entries; manual names retain the allocation, and closing releases it -without renumbering peers. Host deletion runs the shared outbox saga and cancels -sessions, transfers, forwarding, and local history. +Mobile has no Welcome or pane composition, even with Pro: one SSH/SFTP session per Workspace, independent Assistant. +Peer switching preserves connections; Recent selection creates/connects on demand. More owns Rename/Close and +cross-open SSH/SFTP with live paths; explicit close never retries. Initial failure/recovery stays in Journey. +Background expiration closes transports but retains intent; foreground resumes existing Workspaces. -One serialized Live Activity represents only current Workspaces with counts and up to -six distribution icons. Validated links open a Workspace by ID or navigate to the list -without restoring sessions, and carry no secrets or commands. Cold launch ends leftover -activities; their URLs can still restore a saved Workspace. Explicit opening retains a -start intent until foreground conditions allow a request. Closing the last current -Workspace clears that intent and ends the activity even when recent entries remain. -Ordinary foreground updates cannot recreate a dismissed activity after its start intent -has been consumed. Immutable activity snapshots are separate from observable -Workspace/Vault state, and starts, updates and ends share one serial queue. An activity -cannot keep SSH or Agent runs alive. A stale link with no matching entry opens the list -with an unavailable explanation. Restoration does not distinguish system termination -from a user force-quit. +`mobile.workspaces.v1` stores ordered device-local descriptors (stable ID, Host ID, kind, title, duplicate index), +never commands/credentials/paths/terminal contents/transfers. After Hosts load, cold launch fills Recent only, +creating no sessions/connections/history. One MainActor open-by-ID reuses or registers/connects the requested entry, +preserving ID/history-once across scenes; failures need explicit retry. Current/unopened entries share one saved +order, and saving live instances must not discard unopened records. -Mobile Settings exposes App, Terminal, SFTP, AI, and Sync. The macOS window-title -preference stays synchronized but has no mobile control; there is no Live Activities -settings page. AI lists Models, Skills, MCP, then Hermes Memory; user skills and MCP use explicit-save drafts. Network -tests/discovery use ephemeral snapshots, cancel on editor/config changes, and cannot -repopulate discarded drafts. Secrets remain in memory until atomic save and roll back -on metadata failure. Mobile does not start the local MCP listener. +Read legacy arrays/version-1 envelopes; missing duplicate indices get lowest free Host/kind indices in array order, +reserving explicit ones, then immediately write the envelope even if empty. Only successful loads allow saves. +Prune absent Hosts; corrupt/unknown-version bytes remain and block writes until successful load. Close/normal SSH +exit/Host delete removes current and unopened entries; Close All also clears Recent, not connection history. History +actions create new sessions and its section shows only when current/Recent are empty. Titles reserve indices across +both; manual names keep allocation and closing releases without renumbering. Shared Host-delete outbox cancels +sessions/transfers/forwarding/local history. -Each mobile scene owns one navigation/command model and one Assistant library model, -including its selected conversation and New Chat draft. The app-wide Assistant runtime -and conversation store remain shared so active runs are never duplicated. The scene -model is published as a focused value, so hardware-keyboard commands never operate -another iPad window. Home Screen quick actions enter through a per-window scene -delegate, are consumed once by that scene's navigation model, and never target another -iPad window. Connect is static; up to three dynamic history actions derive from the -shared event log. Workspace History and those actions keep the newest display row per -Host and connection kind without deleting duplicate persisted events. Mobile -uses the macOS shortcuts for capabilities that exist on both platforms: navigation, -connection/creation, terminal Find and Command Generator, Workspace close/switch, -and Assistant send/stop. Workspace switching never reconnects the session or claims -terminal focus. The UIKit terminal surface owns F3/Shift-F3 and replaces -Ghostty's bare/Shift-Escape commands only while Find is presented; otherwise Escape -continues to the terminal unchanged. +One serialized Live Activity represents current Workspaces only, counts and at most six distribution icons. +Validated secret/command-free links select ID/list without restoring unrelated sessions; cold launch ends leftover +activities but their URLs may restore a saved entry. Explicit open retains start intent until foreground permits; +last current close ends activity/intent even with Recent entries. Ordinary foreground cannot recreate a dismissed +activity after intent consumption. Immutable snapshots stay separate from observed Workspace/Vault state; start/ +update/end share one queue and never keep SSH/Agent alive. Stale links show list/unavailable explanation. Restoration +cannot distinguish system termination from force-quit. -Mobile Agent runs belong to the app, not the visible page. Conversations retain -independent drafts/runs; archiving persists metadata before navigation and becomes -read-only until restored. Attachment parsing and speech are injected services; speech -appends without sending and cancels on interruption, conversation change, or -background. Mobile shares desktop-independent tool registration and permission/ -secret interaction, but finite background expiration cancels runs, drains events, -closes Agent transports, and never replays or auto-approves work on foreground. +### Scene services and interactions -ListViewKit is an app-owned implementation detail: macOS uses native bridges for the -streaming transcript, large SFTP lists, conversation library, and tool sidebars; iOS -uses one UIKit bridge for transcript and large SFTP lists. Bounded navigation and -archive pages remain SwiftUI `List`. Shared frameworks never import ListViewKit, and -architecture tests own its allowlist. +Settings exposes App, Terminal, SFTP, AI, Sync; synchronized macOS window-title setting has no mobile control and +Live Activities has no settings page. AI order: Models, Skills, MCP, Hermes Memory. Skills/MCP use explicit drafts; +ephemeral tests/discovery cancel on editor/config changes and cannot revive discarded drafts. Secrets stay in memory +until atomic save with rollback on metadata failure. Mobile never starts the local MCP listener. -Mobile workspace file drops use one upload policy for native routing and payload -loading. The visible workspace marks its bounds for a window drop interaction; -the terminal adapter forwards file drops to that destination instead of Ghostty's -local-path paste, while text and web links keep Ghostty's behavior. Leaving the -destination restores the original interaction and clears targeting. File-provider -representations are copied within their callbacks into app-owned staging before -upload preparation; queued transfers retain those files after navigation away. +Each scene owns navigation/commands and Assistant library, selection/New Chat draft; runtime/conversation store is +app-wide to prevent duplicate runs. Focused values route keyboard commands to that scene. Per-window delegates +consume Home Screen actions once; Connect is static and up to three dynamic history actions use the shared log. +History/quick actions display newest Host/kind rows without deleting events. Shared capabilities use desktop +shortcuts: navigation, connect/create, Find/Command Generator, close/switch, Assistant send/stop. Switching never +reconnects or claims terminal focus. UIKit owns F3/Shift-F3 and overrides Ghostty bare/Shift-Escape only during Find; +otherwise Escape passes through. -Mobile top-level destinations are registered in `MobileFeatureRegistry`. A -registration owns stable identity, label, symbol and its destination factory; -registration order is shared by the tab bar and sidebar. The root shell renders that catalog and does not -maintain separate tab, sidebar, and destination switches. Registries are injected -values rather than global singletons, and duplicate feature IDs fail immediately. +Agent runs outlive pages with independent conversation drafts/runs. Archive persists metadata before navigation +and stays read-only until restored. Injected attachment/speech services append speech without sending; interruption, +conversation change/background cancels it. Shared tools/permissions/secrets retain desktop-independent policy; +finite background expiration cancels runs, drains events, closes Agent transports and never replays/auto-approves +on foreground. -Mobile Workspace descriptors use Domain's `ConnectionHistoryEntry.Kind` as the -single SSH/SFTP kind vocabulary. Host Picker and Snippet operation state live in -the mobile Application layer rather than beside their views. Live Metrics samplers -enter through `MobileMetricsServices`, while both app shells obtain Agent host -resolution, authentication, connections, and background commands from the shared -Remote Infrastructure `AgentHostServices` composition. - -Application derives flattened Host and Snippet sections once for both platforms. -Presentation owns the shared command-history subtitle, Vault sync status label, -SFTP transfer status labels, and the exported SFTP drag type. Transfer labels distinguish -upload/download while running and upload/download/copy on completion; other states -reuse the generic Domain label. App targets retain native card/list composition, -selection, menus, and clipboard behavior. - -### Assistant transcript presentation parity (2026-09-12) - -TermindPresentation owns tool summaries, batch status semantics, bounded result previews, timestamp formatting, -Markdown sanitization/soft-break policy and the shared disclosure/deadline state. Hosts inject a resolved -AgentToolDetailPresenter from TermindRemoteInfrastructure; Presentation never imports the registry's transport -module. Tool protocol results and persisted schemas are unchanged. - -TFM retains native AppKit rows, Markdown caches and message editing state. TFI retains SwiftUI content inside -its UIKit ListViewKit bridge. A row's presentation revision can change without its data changing: the mobile -bridge reconfigures that row explicitly, invalidates only its height, and retains its UIHostingConfiguration -content view. Display/measurement purpose, Reduce Motion and scene activity cross that hosting boundary. -Only displayed active content starts timers or scanning; the shared scan parameters and three-second reasoning -deadlines cannot restart merely because a row is recycled. Work completion follows the shared default only -after the whole row leaves the viewport, unless the reader has already chosen its disclosure state. - -The direct approval callback stages a tool card idempotently before accepting a response. Queued tool-proposed -and approval-requested notifications update that identity without erasing an already recorded denial. This keeps -UI state independent of whether the event channel or the direct interaction reaches MainActor first. - -Skill and memory tools own their UI-neutral result and approval projections in AgentKit. -Presentation repairs their historical summary semantics only for recognized local tool/presenter -identities. Their compact result preview shares a 600-character/five-row budget across the entire -card and a scaled 180-point viewport. Full-detail expansion is independent, window-owned transcript -state and survives row reuse; Copy retains the full already-sanitized projection. Unknown presenter -IDs and MCP tools never opt into this policy by name. Long approvals instead retain all live -arguments in a scaled 240-point scroll region with actions outside it. Neither preview path reads -live skill/memory storage or changes the original protocol result, authorization, or persisted data. - -Desktop transcript width changes reconfigure mounted rows without replacing the -cached list or following the tail. Reused row constraints and Markdown geometry -track the current content width even when message identity and text are unchanged; -parsed Markdown remains reusable independently of width-dependent measurements. +File drops share native routing/loading policy. Visible Workspace bounds register a window drop interaction; +terminal forwards files there instead of Ghostty local-path paste, while text/web links keep Ghostty behavior. +Leaving restores the original interaction/clears targets. File-provider data is copied inside callbacks to app +staging, retained by queued transfers after navigation. diff --git a/Memory/REFACTOR.md b/Memory/REFACTOR.md index e12449fb..0541a81a 100644 --- a/Memory/REFACTOR.md +++ b/Memory/REFACTOR.md @@ -1,698 +1,488 @@ # Termind Refactoring Backlog -This English-only document is the durable record of completed redundancy removal and deliberately retained boundaries; append new findings, require a net code reduction unless the item is explicitly structural, verify references with `rg`, and preserve SwiftUI/AppKit and platform-specific lifecycle boundaries. - -## Completed Items - -1. **Delete the obsolete SwiftUI transcript tree (2026-08-30):** Removed the unreachable legacy transcript, message, command-card, compaction, and plan presentation subgraph while retaining live timestamp and risk components; the build and targeted Transcript tests passed. -2. **Merge repeated ListView hosting bridges (2026-08-30):** Introduced `HostingListRow` and `HostingListContent` for ordinary SwiftUI-backed rows while retaining dedicated Transcript and Assistant conversation lifecycles; the app build passed before the final parser-verified Connection Log migration. -3. **Consolidate `SSHSessionModel` initializers (2026-08-30):** Routed the two terminal entry points through one private initializer without changing temporary-host semantics; the build passed. -4. **Merge SSH and Serial terminal appearance configuration (2026-08-30):** Centralized shared appearance application in `TerminalViewState.applyAppearance`; the build passed. -5. **Delete redundant SFTP store overloads (2026-08-30):** Removed four `TransferLibraryModel` forwarding overloads in favor of the existing `RemoteSessionHostStore` entry points; the build passed. -6. **Reuse the Jump Host configuration builder (2026-08-30):** Made final SSH configuration reuse `sshJumpConfigurations` instead of duplicating route validation and error wrapping; the build passed. -7. **Share remote-session connection interaction (2026-08-30):** Moved host-key prompts, acceptance, route progress, prompt cleanup, and reconnect presentation into `RemoteSessionConnectionController` while preserving transport-specific teardown; the Debug build and targeted connection/SFTP tests passed. -8. **Merge short tool-free model text collectors (2026-08-30):** Centralized streamed text collection and tool-call rejection for command, title, and compaction services; their targeted tests passed. -9. **Merge provider authorization and session configuration (2026-08-30):** Added shared request authorization and ephemeral-session construction while retaining catalog-specific Vertex and empty-secret rules; targeted Provider and ChatProbe tests passed. -10. **Consolidate SwiftData save/rollback templates (2026-08-30):** Added a narrow `ModelContext.commit` helper while retaining semantic two-stage history commits; targeted Connection and SFTP History tests passed. -11. **Merge libssh2 queue/continuation bridges (2026-08-30):** Shared the byte-identical serial worker bridge between SSH Exec and SFTP workers; the build passed. -12. **Delete remaining legacy Transcript helpers (2026-08-30):** Removed dead streaming, edit-binding, and SwiftUI activity helpers as part of Item 1; targeted Transcript verification passed. -13. **Finish `ConversationComposerState` migration (2026-08-30):** Migrated callers to `conversation.composer` and deleted four compatibility accessors while preserving attachment mutation hooks; the build and Chat Composer tests passed. -14. **Delete the unreachable toolbar search-menu pipeline (2026-08-30):** Removed unused menu-item plumbing, `NSMenuDelegate` behavior, and the unread find-context kind while preserving query and keyboard behavior; the build passed. -15. **Share Agent SSH prompt conversion (2026-08-30):** Moved Journey prompt and passphrase derivation onto `AgentSSHAuthenticationRequest` for both presentation paths; the build passed. -16. **Extract atomic SFTP remote writes (2026-08-30):** Centralized temporary-path creation, handle cleanup, failure unlinking, atomic rename, and progress sampling while keeping data-source loops separate; the build and SFTP tests passed. -17. **Merge remote transfer conflict resolution (2026-08-30):** Shared Keep Both selection, remote-directory handling, and file conflict policy without merging distinct job types; the Debug build and targeted SFTP tests passed. -18. **Consolidate SFTP client leases (2026-08-30):** Centralized saved-versus-borrowed client acquisition and conditional disconnect while retaining coordinator reservation state; the build and SFTP tests passed. -19. **Centralize legacy tool-presentation metadata (2026-08-30):** Replaced scattered historical name mappings with one compatibility table while preserving generic MCP fallback semantics; targeted presentation tests passed. -20. **Delete the test-only presentation action path (2026-08-30):** Removed unused automatic action-message construction and compressed English verb forms around production `actionKind` semantics; targeted presentation tests passed. -21. **Merge tool-detail input unpacking (2026-08-30):** Added narrow object/array helpers in `AgentToolDetailFormatting` while keeping field-specific presenters local; the build passed. -22. **Merge Agent idle sweepers (2026-08-30):** Replaced two timer loops with `AgentIdleSweeper` while callers retain their own registry and emptiness policy; targeted tests passed. -23. **Reuse `SidebarSectionHeader` (2026-08-30):** Extended the shared header for count and collapse state and removed duplicate Assistant/Snippet layouts; the build passed. -24. **Unify Host deletion entry points (2026-08-30):** Routed group deletion through the repository/outbox saga and removed direct snapshot-persistence deletion; the Debug build and targeted repository, Snippet, and Port Forwarding tests passed. -25. **Remove uncalled wrappers and constants (2026-08-30):** Deleted confirmed-unused Agent preflight, settings, metrics, reconnect, and SSH-status declarations after reference scans; the build passed. -26. **Delete `SettingsCollectionPage` (2026-08-30):** Replaced its two construction sites with the byte-identical `SettingsFormPage`; the build and focused Settings tests passed. -27. **Merge fixed-height cursor lists (2026-08-30):** Shared the adapter used by Snippet Target, Host Chooser, and Export Host lists while excluding dynamic and specialized rows; the build and focused list/chooser tests passed. -28. **Share Agent Exec/SFTP authentication retries (2026-08-30):** Extracted a generic authenticated-open loop while preserving explicit connection kinds and result types; the build and all 21 Agent Host Authentication tests passed. -29. **Deduplicate native Transcript risk and confirmation helpers (2026-08-30):** Routed both row implementations through one risk-color function and one destructive-command confirmation function; the build passed. -30. **Merge Glass shape modifiers (2026-08-30):** Replaced three copied implementations with one shape-parameterized private modifier while preserving public APIs and fallbacks; the build passed. -31. **Consolidate Vault envelope persistence (2026-08-30):** Added one helper for insertion/update and upload-state indexing while leaving versioning and merge policy in callers; the build and Vault Sync tests passed. -32. **Share SFTP remote-conflict preflight (2026-08-30):** Centralized remote-directory ordering, descendant skipping, and target-kind lookup while retaining separate prepared job types; the build and focused SFTP tests passed. -33. **Centralize effective Vertex mode and credential selection (2026-09-04):** Added `usesVertexEndpoint` and `effectiveCredentialRef` to `ModelProvider`, later adapting the former to API-format-based providers; build and Provider verification passed. -34. **Delete newly confirmed dead declarations and decoded metadata (2026-08-30):** Removed unused onboarding, Agent, color, trial-test, and runtime Linux-catalog fields while retaining provenance in generated JSON; the build passed. -35. **Derive Conversation run presentation (2026-09-01):** Replaced manually synchronized run/streaming state with derivation from one reported state and outstanding queues; state-focused tests, localization, and diff checks passed. -36. **Delete tests that freeze user-facing copy (2026-09-02):** Removed 20 copy-only tests and isolated literal assertions while retaining behavioral, persistence, localization-selection, and protocol coverage; the affected target compiled and focused tests passed. -37. **Share checked provider HTTP execution (2026-09-02):** Centralized transport-error translation, HTTP validation, and non-2xx handling for model catalogs and probes while keeping request construction and parsing local; the build and all 12 focused tests passed. -38. **Delete the Finder-only native drop wrapper (2026-09-02):** Removed the unconstructed `NativeFileURLDropTarget` while retaining the generalized target and test-facing AppKit initializer; the build and all five File Drop tests passed. -39. **Centralize Context Chip recovery rules (2026-09-02):** Shared payload-boundary detection and fallback filename derivation between live attachments and restored references; the build and focused recovery tests passed. -40. **Finish save-or-rollback reuse (2026-09-02):** Added `ModelContext.savePendingChanges` and reused `commit` for staged Agent, Outbox, AI snapshot, and conversation mutations without changing semantic save boundaries; all 46 focused tests passed. -41. **Share transient overlay chrome (2026-09-02):** Replaced three identical surface modifier chains with `termindTransientOverlaySurface` while keeping content and transitions local; the Debug build passed. -42. **Share SSH form chrome (2026-09-02):** Replaced two identical conditional padding/glass/border branches with `connectionFormChrome` while preserving separate form behavior; formatting and the Debug build passed. -43. **Share Snippet card content (2026-09-02):** Extracted `SnippetCardContent` for the normal card and drag ghost while keeping their interaction and outer chrome distinct; formatting and the Debug build passed. -44. **Share SFTP read-handle lifecycle (2026-09-02):** Routed inline reads and both download paths through `withRemoteReadHandle` while preserving their distinct loops and destination policies; the build and all 47 focused SFTP tests passed. -45. **Share select/deselect-all helpers (2026-09-02):** Added `ManagedChooserState.allChecked(in:)` and `toggleAll(in:)` for the three identical-enabled-set callers while leaving restricted selection unchanged; the build, localization, and diff checks passed. -46. **Extract AgentKit (2026-09-02):** Moved the platform-neutral Agent runtime into the separately versioned package after introducing host-tool, interaction, prompt, and presentation seams; package build/tests/formatting and the app build/full suite passed. -47. **Collapse Workspace navigation commands (2026-09-02):** Removed commands shadowed by AppKit, kept the standard workspace shortcuts, and moved split shortcuts off system-owned keys with terminal routing coverage; the build, formatting, and localization checks passed. -48. **Address providers by one URL and API type (2026-09-04):** Replaced vendor-kind/default-path branching with explicit inference/base URLs and `ModelAPIFormat`, migrated persisted schemas safely, and shipped AgentKit 0.4.0/0.5.0; package checks, all 994 app tests, localization, and site build passed. - -## Later Completed Work - -- **StoreKit trial consolidation (2026-09-09):** Replaced the local HMAC/Keychain/CloudKit trial system with StoreKit purchasing and a transaction-scoped clock watermark, producing a net 121-line production reduction with commerce checks passing apart from one unrelated catalog expectation. -- **Remove the unused Show Recents preference (2026-09-09):** Deleted the unread preference, its toggle/group, and unused labels while retaining the shared Sidebar accessibility localization; builds, formatting, localization, and relevant tests passed. -- **Remove low-value test assertions (2026-09-11):** Deleted five redundant tests and literal/enum/catalog snapshots for a net 77-line test reduction while retaining behavioral coverage; all relevant checks passed aside from an intermittent unchanged focus test that passed on rerun. -- **Remove the duplicate screenshot window host (2026-09-11):** Deleted `ScreenshotDemoWindow` and reused the production `WindowGroup` with demo restoration disabled; the full pinned test run, native capture, formatting, and localization checks passed. -- **Share SFTP permission controls (2026-09-11):** Moved permission draft behavior to Domain and a shared SwiftUI grid to Presentation for both editors; regression verification is recorded in `MOBILE_ROLLOUT.md`. -- **Share mobile settings and terminal behavior (2026-09-11):** Moved CloudKit, settings transaction/journal, terminal settings, and command-history behavior into their owning frameworks with only desktop adapters retained; shared/mobile tests passed and full macOS verification retained known Agent timing failures. -- **Share Metrics and connection presentation (2026-09-11):** Moved monitoring/Journey state to Application and portable panels/forms to Presentation while retaining native keyboard and log handling; shared/mobile and 127 focused macOS tests passed. -- **Share AI settings business logic (2026-09-11):** Moved the skill library, importer, and role form into Application, Infrastructure, and Presentation respectively while retaining native editors and panels; the isolated build and 81 mobile/shared tests passed. -- **Share Assistant execution (2026-09-12):** Moved runtime, tool adapters, attachments, speech, conversation projection, and transcript list behavior into owning frameworks for both apps; builds, Assistant/navigation tests, localization, and formatting passed apart from an unrelated mobile Metrics race that passed on rerun. -- **Remove app-target forwarding aliases (2026-09-12):** Deleted 86 pure-forwarder files, 224 app aliases, and 20 Persistence compatibility aliases while preserving six responsibility-named adapters; both builds, architecture guards, and the complete TFI test action passed. -- **Align the mobile target with desktop layers (2026-09-12):** Reorganized TFI into the shared layer model, moved connection construction to Infrastructure, and centralized interface-idiom policy; both builds, architecture tests, and the complete TFI tests passed. -- **Share terminal draft and generation lifecycles (2026-09-12):** Extracted `TerminalCommandDraftController` and `CommandGenerationCoordinator` with shared timeouts, cancellation, and protocol constants; focused mobile and desktop tests passed. -- **Separate remote-directory policy from SFTP transport (2026-09-12):** Moved `RemoteFile` to Domain, introduced an Application `RemoteFileSystem` port, and extracted `MobileSFTPSessionModel`, accepting a structural net increase to enforce the dependency boundary; builds, architecture guards, and SFTP tests passed. -- **Finish mobile adoption of shared connection policy (2026-09-12):** Made `RemoteSessionConnectionController` own mobile Host Key decisions and route progress and shared temporary-key preparation between Workspace and Metrics; focused connection, Journey, Metrics, and recovery tests passed. -- **Share terminal Find state (2026-09-12):** Replaced three desktop/mobile query state machines with one Application `TerminalFindController` while retaining platform binding closures; direct and platform tests passed. -- **Derive remote distributions once (2026-09-12):** Centralized Linux distribution detection in Domain with explicit unavailable-versus-conclusive results; Domain, Host projection, and mobile Workspace tests passed. -- **Share platform-neutral Connection Journey sections (2026-09-12):** Moved header and status sections to Presentation while retaining native logs, failures, backgrounds, and keyboard handling, reducing production code by 98 lines; builds and Journey tests passed. -- **Own Ghostty appearance in Presentation (2026-09-12):** Replaced duplicated desktop/mobile theme conversion with one shared bridge while retaining desktop preference storage; both builds and terminal theme tests passed without dependency changes. -- **Share terminal-upload availability and notices (2026-09-12):** Centralized readiness policy and notice presentation while retaining distinct desktop and mobile execution; direct policy tests and both builds passed. -- **Share Known Host presentation titles (2026-09-12):** Moved hashed-address versus endpoint title selection to Presentation while leaving platform cards distinct; direct presentation tests and both builds passed. -- **Restrict ListViewKit to native-list workloads (2026-09-12):** Removed eight unnecessary macOS adapters and eleven mounts, later retaining narrow UIKit transcript/history uses, for a net 611-line macOS reduction; builds, focused tests, formatting, localization, and boundary checks passed. -- **Share Assistant transcript presentation (2026-09-12):** Moved platform-neutral tool/result, disclosure, timestamp, and Markdown policy into Presentation while retaining native renderers and viewport ownership; architecture records and both builds verify the boundary. -- **Remove Periphery-confirmed dead code (2026-09-12):** Deleted 34 imports, three empty files, and confirmed-unused declarations for a net 150-line production reduction while preserving dynamic and protocol entry points; builds, focused tests, formatting, localization, and diff checks passed. -- **Remove UI implementation-type assertions (2026-09-12):** Deleted tests and helpers that pinned concrete AppKit/UIKit/ListViewKit types while retaining behavioral rendering, identity, reuse, interaction, and environment coverage. -- **Remove obsolete app localizations (2026-09-13):** Deleted 81 compiler-confirmed unused catalog entries and three obsolete test expectations and cleared only verified stale metadata; both builds and all localization/language checks passed. -- **Register cross-platform Assistant slash commands (2026-09-13):** Replaced duplicate desktop/mobile command catalogs and execution switches with `ChatSlashCommandRegistry` and explicit platform registration; both builds and focused registry, desktop, and mobile tests passed. -- **Register mobile feature destinations (2026-09-13):** Replaced separate tab/sidebar metadata and destination switches with an injected `MobileFeatureRegistry` while keeping it app-owned; the iOS build and all 28 focused navigation tests passed. -- **Share remaining mobile and desktop policies (2026-09-13):** Centralized tree layout, route planning, Host deletion, display projections, Snippet/model rules, SFTP drag values, and service composition in their owning layers while retaining native UI and transport lifecycles; both builds and focused shared/mobile tests passed. -- **Move trials to annual-subscription introductory offers (2026-09-13):** Retired the zero-price trial product for new sales, retained it only as a legacy entitlement, and made offer presence, eligibility, duration, price, period, and expiration come entirely from StoreKit annual-subscription metadata. -- **Remove brittle and redundant unit tests (2026-09-13):** Removed 27 source-text UI snapshots from `MobileNavigationParityTests`, seven more app source snapshots, real Keychain and status-item integration tests coupled to host state, and duplicated shared permission/history coverage while retaining architecture, persistence, behavioral, and opt-in screenshot checks. Shared credential-storage compatibility, permission defaults, and History limits now live in `FrameworkTests`; the change removes a net 1,303 lines. All 1,079 macOS app tests, 13 focused shared tests, 218 iOS tests, strict formatting, localization, and diff checks passed; the pre-existing `SpeechWaveformViewTests` bitmap-equality failure remains outside this cleanup. -- **Remove standalone-trial compatibility (2026-09-14):** A fresh reference scan confirmed that the retired product identifier, legacy domain value, clock guard, and two legacy entitlement states formed one isolated compatibility path with an estimated 90-line production reduction. Deleted that path, its local StoreKit product, UI states, tests, and copy; renamed the permanent identifier to `lifetimeProductID`; and retained annual introductory trials as the sole trial mechanism. Strict formatting, localization, site build, all 1,091 macOS app tests, the rerun Metrics suite, and all 187 shared iOS tests passed. The iOS target built, while 10 unrelated toolbar-inspection tests remained failing in both the full run and an isolated rerun. - -## Deliberately Retained Boundaries - -- Native Transcript/AppKit rendering, Assistant conversation rows, and specialized dynamic or drag/drop lists keep dedicated lifecycles instead of being forced through generic SwiftUI hosting abstractions. -- SSH, SFTP, Serial, desktop, and mobile implementations share only platform-neutral policy; transport startup, credential persistence, teardown, logs, interaction, and native presentation remain separate where behavior differs. -- SwiftData historical schemas remain frozen migration contracts, small structurally similar repositories and views remain separate when abstraction would not reduce code, and `RuntimeVersions` stays platform-local to avoid introducing an invalid Ghostty dependency. - -The 2026-09-02 completion and re-verification scans found no further independent dead-code or exact-duplication change that met the backlog's reduction and boundary-preservation rules; later work should be appended only after a fresh reference scan. - -## Fresh Refactoring Pass (2026-09-16) - -- **Reuse the shared SFTP download planner — completed.** Evidence: `rg -n 'PreparedJob|PreparedBatch|pendingBatch|keepBothLocalURL' TFM TFI Frameworks TFMTests TFITests` found only one desktop `PreparedJob`/`PreparedBatch` construction path, in `SFTPSessionModel.prepareDownload`, with direction fixed to `.download` and both remote-directory collections empty. Uploads already used `SFTPLocalUploadPlanner`. `SFTPRemoteDownloadPlanner` already owned the equivalent download traversal, local conflict policies, scoped destination access, directory creation, and transfer enqueueing for mobile. Adopted that planner in the desktop session and removed the obsolete mixed-direction batch and local helpers, retaining desktop pending-state/error handling, remote-copy policy, and transfer lifetimes. Estimated production reduction: 180 lines; actual reduction: 167 lines, or 59 lines including the added regression tests. Directory-only downloads now create their empty directory trees because the shared batch retains its destination independently of file jobs. The architecture record documents that ownership. - -Verification completed on 2026-09-16: - -- All 75 SFTP and architecture tests passed, including three new parameterized/behavioral tests covering ten file/directory conflict, subtree, empty-directory, and cancellation cases. The empty-directory regression fails against the pre-refactor `SFTPSessionModel` and passes with the final implementation. -- The full pinned TFM test action built successfully and passed 1,337 of 1,343 tests. Four failures also reproduce with the pre-refactor production source: `SettingsSyncTests.providedDatabaseCopyMigratesWithoutReset` (expects schema 15, current schema is 17), `ModelCapabilityResolverTests.reportedFieldsWinOverCodexAutoReviewOverride`, `ScrubberWebClientTests.fetchPreflightNeverEscalatesPrivateHTTP`, and `ChatComposerStateTests.aFailureNoticeIsNotAnAnswerWorthNaming`. `MetricsJourneyTests.offlineRestoredMetricsReconcileOnMainActor` passed on rerun. `SSHImportTests.indexedIdentityPlanningHandlesTheImportFileLimit` exceeded its two-second wall-clock limit when run concurrently, but passed in isolation against both versions (0.100 seconds with the final source). -- The full pinned TFI test action on iPhone 17 Pro / iOS 26.5 built successfully and passed 426 of 432 tests, including all 205 framework tests. The unchanged mobile graph retained five failures on isolated rerun: `MobileVaultFeatureTests.importRequiresAnExplicitUsernameWhenTheConfigurationOmitsIt` and four `MobileAssistantTests` cases (`completeRegistryIncludesRemoteAndBuiltInToolsOnlyForCapableModels`, `firstNewConversationSendPromotesDraftAndStartsRun`, `attachmentOnlyFirstSendPromotesTheConversation`, and `existingConversationSendStillUsesTheDraftExecutionPath`). `MobileEditorToolbarTests.iconButtonsKeepTheirNamesAndSaveLifecycle` passed on rerun. -- Strict Swift formatting across all production/test targets, `Scripts/check-zh-localization.sh`, the removed-symbol reference scan, and `git diff --check` passed. Both full test actions retained `-onlyUsePackageVersionsFromResolvedFile`; no dependency, localization, or shared production source changed. - -## Follow-up Reuse and Test Cleanup (2026-09-16) - -- **Reuse the shared remote-copy planner — implemented; full verification remains partial.** Evidence: `rg -n 'PreparedRemoteCopy|pendingRemoteCopyBatch|SFTPRemoteCopyPlanner.enqueue|resolveRemoteDirectories' TFM TFI Frameworks TFMTests TFITests -g '*.swift'` confirms the desktop-only batch types and duplicated preparation, conflict resolution, and enqueue loops in `SFTPSessionModel`; mobile already uses `SFTPRemoteCopyPlanner`. Replaced the desktop path with that planner and returned directory-mutation information so empty-directory copies still refresh the browser. Retained app-owned pending state, error handling, transfer completion refresh, and the interactive deletion lifecycle. Estimated production reduction: 220 lines; actual reduction: 193 lines. Added behavioral coverage for file/directory and subtree conflicts across all three policies, empty-directory refresh, and pending-copy cancellation; retained the planner's cancellation and validated recursive deletion checks. Upload conflict helpers remain a separate candidate rather than widening this change. -- **Remove copy-only and duplicated test expectations — implemented; full verification remains partial.** Evidence: `rg -n 'roleLabelsMatchDesktopDefaults|cautionLabelAlsoFitsUnknownCommands|copyApprovalLabelsAndOverwriteWarningUseAppResources|approvalDetailsLocalizeBothEndpointsAndDestructiveWarning|handshakeFailureDescriptionsDistinguishBannerConditions|uploadNoticePresentationUsesOneCrossPlatformMapping' FrameworkTests TFMTests TFITests -g '*.swift'` identifies role-label snapshots, a duplicate unknown-command case, repeated cross-platform approval copy checks, and mixed behavior/copy tests. Removed pure copy assertions and duplicate cases, retained unknown-command classification in its parameterized suite, and consolidated approval endpoint values and destructive-warning policy coverage across policies/locales. Preserved diagnostic context, upload gating, and notice payload/state coverage. Also dropped isolated fixed title/icon assertions from otherwise behavioral tests. Estimated test reduction: 50 lines; actual cleanup reduction: 59 lines, excluding 75 net lines of remote-copy regression coverage. Total Swift reduction: 177 lines. Retained architecture guards, language-selection/catalog validation, protocol strings, user-data round trips, and behavioral assertions that contain strings. - -Verification and remaining limitations (2026-09-16): - -- The pinned TFM focused test action passed all 200 tests, including SFTP/session/copy, architecture boundaries, tool presentation, handshake diagnostics, SSH import, toolbar behavior, app language, upload availability, and command classification. Result: `/tmp/termind-refactor-copy-focused-20260916.xcresult`. -- The pinned full TFM test action built successfully and passed 1,436 of 1,437 tests. The sole failure is the unchanged `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable` bitmap equality assertion. An earlier backlog entry records this same test failure; no baseline or isolated rerun was performed in this pass. Result: `/tmp/termind-refactor-copy-macos-20260916.xcresult`. -- The full TFI action did not build or execute tests: the supplied simulator destination UUID was unavailable. This is not a passing iOS verification. A fresh device listing found an available booted iPhone 17 Pro, but the owner requested an immediate commit before rerunning either platform. -- Strict Swift formatting across all production/test targets, `Scripts/check-zh-localization.sh`, the removed desktop-helper reference scan, and `git diff --check` passed. Xcode successfully resolved modules that standalone SourceKit could not find. Existing generic SwiftLint file/type/function-size and complexity findings remain outside this independent refactor; no inline suppressions were added. Dependencies and localization catalogs are unchanged. -- Submitted at the owner's explicit request with these verification limitations retained rather than deleting unrelated behavioral tests to obtain a green result. - -## Remove Low-Value UI and Source Assertions (2026-09-16) - -- **Implemented; full verification in progress.** Evidence: `rg -n '#filePath|sourceRoot|String\(contentsOf|source\.contains' FrameworkTests TFMTests TFITests -g '*.swift'` found 35 architecture tests plus eight source snapshots in Color System, Menu Symbol, SSH Import, Keychain Order, Commerce Configuration, and Mobile Navigation Parity. At the owner's request, removed implementation-spelling/layout snapshots, enum and palette echoes, fixed-canvas render checks, disconnected standard-library sorting tests, and orphaned helpers; trimmed literal copy from mixed behavioral tests. Removed 52 test declarations and a net 1,230 Swift lines against an estimated 1,000–1,200-line reduction. Source assertions dropped from 43 tests to nine narrow module/dependency and ownership guards. The ListViewKit import allowlist now permits removing an importer without requiring a snapshot update, while still rejecting new unapproved importers. Retained language/catalog validation, protocol/data contracts, behavioral lifecycle/rendering coverage, and opt-in screenshot capture. Grouped host-list coverage now checks row placement using fixture data rather than localized headings. No production, dependency, or localization catalog changed; existing architecture-document edits were preserved. - -Verification in progress: - -- Initial pinned Xcode checks passed all 18 selected macOS tests and five selected iOS tests, compiling both test targets. Results: `/tmp/termind-test-cleanup-initial-macos.xcresult` and `/tmp/termind-test-cleanup-initial-ios.xcresult`. -- Strict Swift formatting across production/test targets, `Scripts/check-zh-localization.sh`, the remaining-source-assertion scan, and `git diff --check` passed. Active standalone SourceKit checks could not resolve the Xcode test module graph and were partly inconclusive; Xcode compilation is the authoritative check. Pre-existing generic SwiftLint size, tuple, pixel-coordinate naming, and force-try findings were left outside this deletion; no inline suppressions were added. -- Full pinned macOS and iOS test actions are running sequentially. No unrelated behavioral test is being removed to hide a failing result. - -## Share SFTP Transfer Status Labels (2026-09-16) - -- **Implemented and verified with existing iOS failures.** Evidence: `rg -n 'status\(for: transfer\)|transfer\.state\.label' TFM TFI` found a mobile-only direction/state mapping and two generic desktop labels. Moved the agreed running/completed direction labels into Presentation, reused generic labels for other states, and connected both apps to the shared property. Production reduction: eight lines, matching the estimate. Existing translations and transport behavior are unchanged. -- The pinned full TFM action passed all 1,183 app tests and 213 shared tests, including language and architecture checks. Result: `/tmp/termind-sftp-status-macos-20260916.xcresult`. -- The pinned full TFI action compiled both targets and passed 212 of 217 app tests plus all 213 shared tests. Its five failures in `MobileAssistantTests` and `MobileVaultFeatureTests` match the pre-change `/tmp/termind-test-cleanup-full-ios.log` exactly; no unrelated tests were changed. Result: `/tmp/termind-sftp-status-ios-20260916.xcresult`. -- Strict Swift formatting, `Scripts/check-zh-localization.sh`, and `git diff --check` passed. Visually reviewed English, Simplified Chinese, and Traditional Chinese status matrices rendered from the built shared property and app localization resources in `/tmp/termind-sftp-status-preview/`; these are label previews, not live transfer screenshots. - -## Retire the Desktop Forwarding Editor Adapter - -- **Implemented; verification pending (reproduced iOS failures).** Evidence: `rg -n 'PortForwardingEditorState|PortForwardingEditorPolicy' TFM TFI Frameworks TFMTests TFITests FrameworkTests Memory Termind.xcodeproj -g '*.swift' -g '*.md' -g '*.pbxproj'` confirms that the 43-line desktop adapter has only three production consumers, all computed state getters in `TFM/Features/Ports/PortForwardingView.swift`, and seven desktop tests. It already delegates saved eligibility and normalization to Application's existing `PortForwardingEditorPolicy`; its remaining flags derive identically from enabled intent and runtime activity. Mobile already uses the policy directly. Action: adopt that policy in the three desktop getters and delete the adapter, retaining synchronous observable saved-rule/saved-host/controller reads and full runtime values for native presentation. Preserve all callbacks, enabled intent OR activity, draft normalization, saved-host lookup by the editor host's ID, and editing locks. Retarget all seven desktop test behaviors and add shared-policy eligibility/intent/activity coverage. Estimated production reduction: 27–28 Swift lines; test additions counted separately. No architecture invariant changes, boundary-guard edits, public API, dependency, persistence, runtime, approval, secret, or localization changes are required. -- Implementation outcome: removed the adapter without a replacement wrapper; production Swift decreased by 27 lines (43 deleted, 16 net added to the view). All seven desktop test behaviors remain, including the AppModel save-without-start integration and saved-host lookup through a changed draft host's ID. Expanded active-phase coverage across both intent values, local/remote normalization boundaries and missing targets, and invalid hidden dynamic targets. Added 14 shared-policy matrix/eligibility cases. Test Swift increased by 109 lines; total Swift increased by 82 lines, with regression additions reported separately from the production reduction. Architecture records/guards, feature integration suites, native copy, shared production code, and pinned dependencies are unchanged. - -- Reviews: both independent code reviews and the prior final audit found no code issue or requested revision. This documentation audit independently inspected the actual diff and unchanged shared policy and found no behavior or boundary issue. Only this appended entry was updated; every earlier entry is preserved. - -Verification audit (2026-09-16 UTC): - -- The prior pinned focused TFM action passed **33/33 tests**, zero failures/skips: 14 forwarding feature, seven editor, nine architecture, and three shared-policy tests (the latter cover 14 parameterized cases). Independently re-extracted `/tmp/termind-forwarding-adapter-verify.xMtOCK/focused.xcresult` and checked `focused.log` and `focused.command.txt` in that directory; the action was not repeated. -- The full pinned TFM action passed **1,399/1,399 tests** (1,183 TFMTests + 216 FrameworkTests), zero failures/skips, exit 0. Result: `/tmp/termind-forwarding-adapter-recovery.eggzSE/remaining-checks.9wl35uwk/macos.xcresult`. -- The full pinned TFI action passed **428/433 tests** (212/217 TFITests + 216/216 FrameworkTests), five failures, zero skips, exit 65. Result: `/tmp/termind-forwarding-adapter-recovery.eggzSE/remaining-checks.9wl35uwk/ios.xcresult`. The exact five-test pinned rerun passed **0/5**, with the same five identifiers and identical failure text, exit 65: `/tmp/termind-forwarding-adapter-recovery.eggzSE/remaining-checks.9wl35uwk/ios-rerun.xcresult`. Both used the freshly discovered iPhone 17 Pro / iOS 26.5 simulator, UDID `38789AE8-B5AA-46CA-A15C-77CE760FC550`. -- Actual repeated failures (all in TFITests): - - `MobileAssistantTests/attachmentOnlyFirstSendPromotesTheConversation()` and `MobileAssistantTests/firstNewConversationSendPromotesDraftAndStartsRun()`: `Expectation failed: draft.messages.map(\.role) == [.user, .assistant]`. - - `MobileAssistantTests/completeRegistryIncludesRemoteAndBuiltInToolsOnlyForCapableModels()`: `Expectation failed: !names.contains("terminal_suggest")`. - - `MobileAssistantTests/existingConversationSendStillUsesTheDraftExecutionPath()`: `Expectation failed: conversation.messages.map(\.role) == [.user, .user, .assistant]`. - - `MobileVaultFeatureTests/importRequiresAnExplicitUsernameWhenTheConfigurationOmitsIt()`: `Expectation failed: model.canImport`. -- These failures are **baseline-unverified**, not established pre-existing failures or proven regressions. No pre-change baseline was run for this patch; earlier backlog notes are not substituted for baseline evidence. Their triage/baseline attribution and a passing required iOS verification remain blockers to completion. All planned automated stages reached terminal completion after the prior provider interruption; none is missing, and that interruption was not a test failure or a user-requested stop. -- Static checks passed: full strict recursive Swift formatting over `Frameworks TFM TFI FrameworkTests TFMTests TFITests TermindMCPBridge`, `Scripts/check-zh-localization.sh`, and `git diff --check` (all exit 0). The first-party Swift retired-symbol scan found no `PortForwardingEditorState` (expected `rg` exit 1). The new test's whitespace checks had no diagnostics; the runner initially mislabeled no-index exit 1 (files differ), corrected in `untracked-whitespace-adjudication.json` without altering raw evidence, with a supplementary read-only scan passing at exit 0. Implementation-stage evidence remains in `/tmp/termind-forwarding-adapter-implement.aTsWRl/checks.sh` and `checks.log`. -- Integrity: before this documentation update, the complete patch, including the untracked test, matched both the focused verifier's initial patch and `/tmp/termind-forwarding-adapter-recovery.eggzSE/partial.patch` byte-for-byte (SHA-256 `f76b9950aa3bb49140865feec1153836f59ce903d25f3a32edcbf07ab320a691`). Reviewed production/test bytes remain unchanged. `project.pbxproj` and `Package.resolved` hashes match both the saved verification evidence and HEAD; no dependency drift. The same five paths remain uncommitted on `main` at `8b01a631fefdbe14e1324d87c91d0860124d85d3`; no further builds, source/test edits, or commits were made by this audit. -- Limitations/warnings: native manual status/toggle/edit-lock and save/cancel/back testing was **not executed**; layout and copy are unchanged, and no visual evidence is claimed. Full-suite xcresult summaries retain 166 macOS runtime warnings (88 environment-locale, 74 constraint, four StoreKit transaction-listener) and 29 iOS warnings (27 environment-locale, two publishing-during-view-update); the targeted rerun reports none. These warnings were not baseline-attributed or broadly investigated. Focused logs also retain native row-height and Xcode diagnostic warnings despite the passing result. -- Artifacts: `/tmp/termind-forwarding-adapter-recovery.eggzSE/remaining-checks.9wl35uwk/` contains `verification-report.md`, `verification-report.json`, `runner.log`, `runner.completed.json`, per-stage `*.command.txt`/`*.record.json`/logs, `*-summary.json`, `*-tests.json`, `failure-comparison.json`, simulator selection, and postverification patch/dependency evidence. Independent re-extractions, count/failure comparisons, before/after integrity, and the post-documentation `git diff --check` result are preserved in `/tmp/termind-forwarding-adapter-recovery.eggzSE/audit-record/`. - -## Repair the Five Repeated iOS Test Failures (2026-09-17) - -- **Completed and verified.** Before editing, the exact five tests failed on clean `d582d711` using the pinned dependencies and iPhone 17 Pro / iOS 26.5. This establishes the baseline for this repair without retroactively claiming a pre-refactor baseline for earlier entries. Result: `/tmp/termind-ios-five-fixes.NfgsN4/baseline.xcresult` (0/5 passed, exit 65). -- Three `MobileAssistantTests` send cases expected a synthetic assistant message from an in-memory app with no Chat model. `b2f05217` intentionally replaced that message with a failed run and a separate timeline failure notice. The tests now verify user-only history, failed-run metadata, prompt-to-run association, the visible failure row, and exclusion from title generation. The existing-conversation case awaits the actual failed state rather than yielding 20 times for an assistant message that must not exist. -- The registry case expected `terminal_suggest` to be absent on mobile. `df3bd04c` intentionally stabilized definitions across surfaces; the executor gates availability from the delivered user snapshot. The test now requires the tool, compares descriptors with and without terminal availability, and still requires an empty registry for models without tool-call capability. -- The import case directly mutated `items[0].host.username` after scanning. Since `ac165a9a`, scanning also builds indexed Identity previews, so the selected Identity retained its invalid empty username. The replacement uses a temporary config and the real `SSHImportReader` with an explicitly empty default username: invalid input cannot commit anything, then adding `User deploy` and rescanning rebuilds both previews and imports the correctly linked Host/Identity. The obsolete reader fake was removed; no production mutation API was added for a test-only path. -- All five original test identifiers are retained; none is skipped, deleted, or marked as an expected failure. Only the two mobile test files and this record change. Production behavior, architecture invariants/guards, localization catalogs, and pinned dependencies remain unchanged. -- Verification: all five tests passed in three separate Xcode test actions (`fixed-repeat.xcresult`, `focused-2.xcresult`, `focused-3.xcresult`), then the full pinned TFI action passed **433/433** and full TFM action passed **1,399/1,399**, both with zero failures/skips (`full-ios.xcresult`, `full-macos.xcresult`, exit 0). Although the first action supplied `-test-iterations 3`, its log contains only one Swift Testing run; two independent actions supply the actual repetition evidence. This resolves the outstanding automated iOS verification blocker in the preceding entry. -- Full strict recursive Swift formatting, `Scripts/check-zh-localization.sh`, and whitespace checks passed. Standalone SourceKit probes could not confirm the Xcode test-module graph; the successful Xcode compilation and simulator tests are the authoritative checks. Full results retain 29 iOS and 168 macOS runtime warnings, not investigated or baseline-attributed by this test-only repair; the three focused actions have none. Artifacts and the sequential verification script are under `/tmp/termind-ios-five-fixes.NfgsN4/`. - - -## 2026-09-17 — Replace the single-document skill loader - -- Evidence: `load_skill` had its own definition, presenter, catalog guidance, and name-based output exemptions. A package file reader now supplies the same bounded SKILL.md behavior plus supporting resources. -- Action: remove the executable `LoadSkillTool` and register `skill_read_file`; retain only the historical presenter ID and replay compatibility. -- Estimated reduction: remove roughly 100 lines of the superseded loader; package management is an additive feature, not a net code-reduction claim. -- Status: implemented. AgentKit tests pass; application skill, migration, memory lifecycle, and mobile suites have been exercised. - - -## 2026-09-17 — Replace seeded runbooks with immutable skill helpers - -- Evidence: `AgentSkillLibrary` shipped three editable runbooks; `SkillLibraryModel` and `SettingsSyncPersistence` both seeded them, and both apps exposed a destructive library-wide Reset. -- Action: replace the seeds with two runtime-only skills and remove generation seeding, Reset, and old runbook definitions/fixtures. Previously installed starters remain ordinary user-owned records without content rewriting or automatic deletion. Keep conflict repair in native editors and use the existing atomic skill/sync persistence boundary for package installation. -- Estimated reduction: roughly 200 production lines from retired runbooks, duplicated seeding and Reset UI, excluding the new installer capability and migration tests. -- Status: implemented. AgentKit 0.13.0 is signed and published; 213 package tests passed. Existing skills remain user-owned and the Chinese memory label is 持久化记忆 / 持久化記憶. -- Verification: the final iOS run passed all 452 tests. The macOS app run passed 1,183 tests; AppLanguageTests passed again independently. The refreshed shared-framework run passed 231 of 232 tests, with the existing `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable` PNG byte-equality failure remaining. New skill/library/sync tests passed, bilingual native screenshots were reviewed, and strict formatting, Chinese localization and whitespace checks passed. - -## Reuse and Architecture Scan (2026-09-17) - -**Status: discovery only; all six candidates below are pending.** Baseline: clean `main` at `8509ddd5`. -The inventory and normalized-block scan covered 516 tracked first-party production Swift files (113,165 lines) -under `Frameworks/`, `TFM/`, `TFI/`, and `TermindMCPBridge/`. Candidate bodies, consumers, and relevant tests -were then inspected directly; this is not a claim that every file received a behavioral audit. A source import -check across the 273 shared-framework Swift files found no disallowed first-party dependency edge relative to -`ARCHITECTURE.md`. The source graph was stale and Swift structural navigation was unavailable, so neither was -used as proof of reachability or dead code. The website's localized home routes already reuse one page. -AgentKit and other third-party sources were not included. - -Estimates below are prospective **net production Swift reductions**, allowing for a narrow shared implementation -and caller changes but excluding regression tests. They are not measured patch outcomes. Implement and verify -one independent item at a time; abandon a reduction claim if the new abstraction costs more than it removes. -No production, test, dependency, localization, or architecture-invariant change is made by this scan. - -### R1. Share CloudKit record transport coding — first implementation candidate - -- **Evidence:** `VaultSyncCoordinator.swift:475-548`, `SettingsSyncCoordinator.swift:519-594`, and - `MetricsSyncCoordinator.swift:424-498`, all under `Frameworks/TermindInfrastructure/Cloud/`, repeat CKRecord - restoration, metadata assignment, SHA-256 payload hashing, inline-versus-CKAsset storage, secure system-field - archiving, and cache-file construction. Confirm with - `rg -n 'static func (makeRecord|envelope|payloadData|systemFields|record|assetURL)' Frameworks/TermindInfrastructure/Cloud`. -- **Action:** introduce an internal record-coding helper in the same Infrastructure layer. Share transport - mechanics, not the three envelope codecs or their semantic validation. Keep existing public entry points, - record/zone names, record-name casing, the 700,000-byte threshold, separate asset directories, error mapping, - and Settings' legacy Guardian canonicalization. Do not change persistence entities or sync engine ownership. -- **Estimated reduction:** 70–110 lines. **Risk:** low-to-medium; wire compatibility matters even without a migration. -- **Required verification:** retain `VaultSyncTests.cloudRecordUsesAssetForLargePayloadAndRejectsCorruption`, - Settings record round trips and legacy Guardian tests, and `MetricsSyncTests.recordsRoundTripAndRemoteChangesMergePerHost`. - Add shared boundary cases for payload sizes at/above the threshold, corrupt or missing payloads, system-field - restoration, and asset namespace separation; run both pinned app test actions. This estimate excludes R5. - -### R2. Share the remaining upload/copy remote-conflict helpers - -- **Evidence:** `Frameworks/TermindRemoteInfrastructure/Transfers/SFTPLocalUploadPlanner.swift:178-310` repeats - preflight, subtree skip/remapping, conflict resolution, Keep Both naming, and recursive deletion from - `SFTPRemoteTransferPlanner.swift:311-439`. Copy helpers also serve `SFTPRemoteCopyOperation.swift:108,126`. - Confirm with - `rg -n 'remoteConflictPreflight|resolveRemoteDirectories|resolveRemoteConflict|deleteRecursively|SFTPRemoteCopyPlanner\.(resolveDirectories|resolveConflict)' Frameworks/TermindRemoteInfrastructure/Transfers`. - This freshly verifies the upload-helper follow-up left open in the 2026-09-16 remote-copy entry; it is not - another claim that desktop planner adoption remains unfinished. -- **Action:** extract a focused Remote Infrastructure helper using the existing `RemoteFileSystem` port where - sufficient. Keep local upload traversal, remote manifests, their different job values/enqueue operations, - browser pending-state ownership, and interactive deletion separate. Preserve both directory-mutation reporting - and the copy operation's created-directory accounting. -- **Behavioral caveat:** copy helpers check cancellation and validate recursive child names/paths; upload helpers - do not contain equivalent checks. Copy preflight uses self-or-descendant containment while upload preflight - checks descendants. Characterize these differences before sharing; do not weaken the copy checks or describe - any deliberate upload hardening as a byte-equivalent refactor. No failure or vulnerability was reproduced here. -- **Estimated reduction:** 100–140 lines. **Risk:** medium because Replace may delete remote directories. -- **Required verification:** extend `SFTPFeatureTests` upload preflight/subtree tests and retain copy conflict, - empty-directory, pending-cancellation, and `SFTPCopyToolTests` coverage. Parameterize all three conflict policies, - file/directory type collisions, nested path mappings, cancellation, and invalid child paths for both callers. - Retain mobile upload tests, including scoped symlink exclusion and directory-only uploads, and run both pinned suites. - -### R3. Share Pro offer presentation calculations without merging the paywalls - -- **Evidence:** `TFM/Features/Commerce/ProPaywallView.swift:179-219,238-248` and - `TFI/Features/Commerce/MobileProView.swift:212-247,290-300` independently compute purchase titles, annual/trial - disclosures, trial badges, billing units, and localized duration formatting. Confirm with - `rg -n 'purchaseButtonTitle|selectedPlanDetail|trialBadge|annualBillingUnit|func formatted' TFM/Features/Commerce TFI/Features/Commerce`. -- **Action:** add a small value-based projection in `TermindPresentation`, receiving the selected plan, live - StoreKit-derived price/period/offer values, eligibility, and effective locale. Do not import - `TermindInfrastructure` or pass `ProAccessController` into Presentation. Both app views keep their own cards, - feature lists, sheet/navigation state, purchase tasks, and native subscription-management action. -- **Estimated reduction:** 20–40 lines after projection construction at both call sites. **Risk:** low. -- **Required verification:** cover annual versus lifetime, eligible versus unavailable trial, missing product data, - singular versus composite periods, and locale selection without full-copy snapshots. Retain `ProAccessTests` - and `StoreKitTrialTests`, run `AppLanguageTests` and both pinned suites, and review English/Chinese paywalls. - macOS must retain its explicit app-language choice; mobile must keep Apple's per-app language behavior. - -### R4. Complete the existing architectural guards — explicitly structural - -- **Evidence:** `TFMTests/ArchitectureBoundaryTests.swift:29-42,81-148,157-167` checks the AgentKit-owned type list - only under `TFM`; its SwiftData ownership scan omits Remote Infrastructure, Presentation, and the bridge. - The shared Application import guard does not include the full UI/native-I/O prohibition, and Presentation's - guard does not reject `TermindInfrastructure`. Import checks compare entire lines, so annotated or qualified - imports are not recognized. Confirm with - `rg -n 'applicationDoesNotImport|typesOwnedByTheAgentPackage|sharedFrameworksCannot|sharedPresentation|swiftDataModels|swiftFiles|text.split' TFMTests/ArchitectureBoundaryTests.swift`. -- **Action:** centralize the production-root/dependency rules and a bounded import recognizer. Apply existing - ownership rules across all appropriate production roots; recognize ordinary, annotated, and symbol-qualified - imports. Retain fail-closed behavior for missing/empty roots and the native-list allowlist. -- **Benefit and estimate:** no production reduction is claimed; allow approximately 20–60 net test lines if needed - for complete coverage. This closes test coverage gaps, not an observed production dependency violation. - Do not reintroduce source snapshots of UI copy, layouts, retired paths, or shared-component wiring. -- **Required verification:** fixture-based behavioral checks must reject forbidden annotated/qualified imports - and out-of-owner declarations while allowing valid imports and removal of an allowed native-list importer. - Run `ArchitectureBoundaryTests` and both pinned suites; do not mutate production files to exercise negative cases. - -### R5. Reuse CloudKit engine lifecycle implementation — staged architecture follow-up - -- **Evidence:** the same three Cloud coordinators independently own `operationID`, operation serialization, - account preparation, engine creation, fetch-before-send readiness, delegate forwarding, and account/zone reset. - Examples: Vault `127-246,291-357`, Settings `134-247,327-393`, Metrics `129-240,275-334`. - Confirm with - `rg -n 'operationID|prepareAndSynchronize|synchronizeNow|isReadyToSend|stopEngineForAccountChange|handleZoneDeletion' Frameworks/TermindInfrastructure/Cloud`. - Existing sync tests primarily exercise codecs, journals, merge behavior, and preference switches rather than - a deterministic asynchronous engine/account lifecycle. -- **Action:** after R1, investigate a composed, typed Infrastructure driver with per-category persistence/event - adapters. Share implementation, **not instances**: all three zones, journals, account bindings, subscriptions, - category switches, and independent coordinator lifetimes must remain. Settings retains secret migration and - upload authorization at the current fetch/account boundary, plus its legacy-record handling. Avoid an untyped - universal sync framework or changing the frozen SwiftData models to satisfy a generic protocol. -- **Estimated reduction:** 200–350 additional lines, excluding R1's record coding. **Risk:** high; design and - characterization must precede migration. Update the architecture ownership record only when implemented. -- **Required verification:** first add deterministic coverage for pause/retry and stale callbacks, account - switching/confirmation, zone and environment reset, failed fetch, readiness before sends, and Settings secret - authorization/revocation. Migrate one category at a time, retaining quarantine, tombstones, and versioned-upload - acknowledgement tests. Record codec round trips alone are not sufficient proof for this change. - -### R6. Share MCP transfer ticket policy, not transfer execution - -- **Evidence:** `TFM/Application/MCPServer/MCPUploadCoordinator.swift:42-135,224-246,374-409` and - `MCPDownloadCoordinator.swift:36-128,364-403` duplicate session/host ticket bookkeeping, ten-minute expiry, - 64 unfinished tickets per direction, thirty-minute retention, 256 terminal records per direction, and activity - eligibility. Download also references `MCPUploadRequest.maximumBytes` at line 243 despite the limit applying - to both directions. Confirm with - `rg -n 'has.*Activity|count < 64|addingTimeInterval\(600\)|maximumBytes|func (endSession|revokeSession|sweep)|1_800|count - 256' TFM/Application/MCPServer/MCP{Upload,Download}Coordinator.swift`. -- **Action:** extract only common metadata/retention/limit policy within the macOS MCP implementation, reusing - the existing shared `MCPTransferBudget`. Keep protocol-specific claim checks, response/error payloads, attempt - generations, read/write loops, cleanup, and cancellation tasks separate. Do not move the local listener into - a mobile/shared framework or AgentKit, and do not introduce a broad generic ticket base class merely to rename fields. -- **Behavioral caveat:** expired uploads may need asynchronous partial-file cleanup; downloads can re-serve a - Range after success and retain revocation independently. The two `cancelAndWait` methods capture tasks at - different times, and upload cancellation can create cleanup work. These paths must not be mechanically merged. -- **Estimated reduction:** 30–70 lines if a narrow metadata/policy extraction pays for itself. **Risk:** medium-to-high; - authorization, retention, and capacity release remain security/lifecycle boundaries. -- **Required verification:** retain `MCPUploadTests`, `MCPDownloadTests`, and session teardown tests. Add shared - clock-boundary/retention cases while retaining the distinct range-after-success, acknowledged upload-offset, - late-disconnect, session/host revocation, and four-slot-until-cleanup behaviors. Preserve public protocol strings - and metadata-only audit behavior. - -### Scan disposition and verification scope - -- Start with **R1**, then consider R2 or the smaller R3; address R4 before expanding dependency/lifecycle ownership. - R5 and R6 need separate characterization/design work, not one large combined refactor. -- Retain frozen persistence shapes, responsibility-named Vault facades, native transcript/list renderers, platform - terminal/session teardown, and platform-local runtime-version adapters. Large files alone are not evidence for - a code-reducing extraction. The small pixel-inspection duplicate does not justify merging native renderers. -- Discovery used source reads, fresh `rg` references, normalized-block signals, and an import-direction audit. - No Xcode tests, application builds, or visual checks were run by this documentation-only scan; prior results - above are not reused as verification of these pending candidates. -- Documentation verification passed: the previous backlog is preserved byte-for-byte, the addition is English-only, - referenced source paths were checked, and `git diff --check` plus `Scripts/check-zh-localization.sh` passed. - Only this backlog file changed; all six candidates remain unimplemented and unverified at runtime. - -## Ordered Implementation Follow-up (2026-09-17) - -The operator approved R1, R2, and R3 in that order. R4–R6 remain outside this implementation pass. - -### R1: CloudKit transport coding — implemented, affected checks passed - -- Added internal `CloudSyncRecordCoding` and adopted it in Vault, Settings, and Metrics. Record restoration, - secure system-field archiving, lowercase SHA-256, raw/quarantine versus validated payload reads, the exact - inline threshold, lazy asset creation, and namespaced asset paths now have one implementation. -- Per-envelope metadata assignment deliberately remains at each coordinator, alongside semantic validation and - Settings' legacy Guardian canonicalization. This avoids a large generic metadata API. Public entry points, - error mapping, record/zone names, cleanup, persistence models, and independent sync lifetimes are unchanged. -- Measured production change: **40 fewer lines**, including the new helper (1,741 to 1,701 lines across the four - production files), rather than the discovery estimate of 70–110. Added eight focused test functions with - twelve parameter-expanded cases for boundaries, hash compatibility, storage transitions, corruption/raw - quarantine access, missing payloads, asset I/O errors, system fields, and asset namespace isolation. -- Pinned Xcode focused actions passed: **macOS 61/61**, **iOS 13/13**; both actually executed the new suite, and - source snapshots were unchanged throughout each action. Existing Vault/Settings/legacy Guardian, Metrics, - and architectural tests in the selected actions passed. Evidence: `/tmp/termind-refactor-three.7kUsqP/r1-focused-*` - contains commands, logs, `.xcresult` bundles, test trees, summaries, and source snapshots. Strict formatting - and whitespace checks passed. Standalone SourceKit cannot resolve Xcode test/framework imports; actual - Xcode compilation succeeded. Pre-existing coordinator-size advisories are not a reason to implement R5. -- Full-suite baseline was captured before any production edit: macOS **1,502/1,505** and iOS **502/503**. - Failures were the parallel approval cancellation, runtime tool cancellation, metrics worker lifecycle, and - mobile knowledge-row expansion tests. These are separate baseline issues, not verification passes; final - full-suite comparison remains pending. No AgentKit dependency or unrelated production/test repair was made. - -### R2: Remote conflict resolution — implemented, affected checks passed - -- Before changing Remote Infrastructure, new mobile upload cases reproduced the two guard gaps with a fake - backend: a cancelled directory-only enqueue still created a directory, and Replace accepted a child outside - the conflicting directory. Existing copy/SFTP feature checks passed **65/65** on macOS; mobile upload checks - passed **5/7**, failing only the two new cases. Both action source snapshots were unchanged. Evidence: - `/tmp/termind-refactor-three.7kUsqP/r2-before-*` (commands, logs, result bundles, test trees, snapshots). -- Added `SFTPRemoteConflictResolver` against the existing `RemoteFileSystem` port, retaining both preflight - counting modes: upload blocks strict descendants, while copy also blocks the conflicting root itself. - Upload deliberately gains the existing copy cancellation and recursive-entry checks; this is characterized - hardening, not identical old behavior. Manifests, scoped local traversal, enqueue lifetimes, created-directory - accounting, and existing error/context reporting remain separate. The legacy copy-source error moved to its - own file without changing its type or localized text. -- Measured production change: **139 fewer lines**, including both new files (906 to 767 lines across the five - production files). Seven new shared test functions cover seventeen parameter-expanded cases for both preflight - modes, all conflict policies, subtree boundaries/mapping, overwrite flags, directory accounting, deletion order, - invalid child entries, and existing cancellation checkpoints. `FrameworkTests` now links Remote Infrastructure - to exercise the helper on both platforms; the production dependency graph and package pins did not change. -- Post-change pinned actions passed **macOS 81/81** and **iOS 23/23**, including the two previously failing mobile - upload cases and the new shared suite on both platforms. Sources were unchanged throughout each action. - Evidence: `/tmp/termind-refactor-three.7kUsqP/r2-focused-*`. Strict formatting, project plist validation, - localization, and whitespace checks passed. The architecture record now documents the shared resolver and - retained counting rules. The unrelated full-suite baseline failures remain tracked for final comparison. - -### R3: Pro offer presentation — implemented, affected checks passed - -- Added the value-only `ProOfferPresentation` from StoreKit-derived price/period/offer data, eligibility, and - the selected plan. No StoreKit controller, native purchase task, layout, or navigation state moved layers. -- The two localization policies remain separate: macOS supplies its explicit `AppLocalization` lookup; mobile - uses the initializer's Foundation/Apple-selected bundle lookup. Both retain their effective locale for Gregorian - duration formatting, full unit names, and one displayed component. Each purchase section computes the projection - once. The existing History duration formatter remains separate (abbreviated/two units). -- Measured production change: **32 fewer lines**, including the new projection (602 to 570 across three files). - Nine new shared test functions exercise twenty parameter-expanded cases for purchase/trial branches, missing - product data, lifetime behavior, singular/composite periods, unit priority, and independent formatting/lookup - policies. An additional six-case `AppLanguageTests` check uses the app's real English, zh-Hans, and zh-Hant - catalogs for both plans. No catalog entries or translations changed. -- Pinned focused actions passed **macOS 41/41** and **iOS 11/11**, including the new suite on both platforms, - explicit app-language cases, access/trial policies, architectural guards, and mobile language preference tests. - Both actions retained unchanged source snapshots. Evidence: `/tmp/termind-refactor-three.7kUsqP/r3-focused-*`. - Native layout/action wiring was reviewed in the diff; no interactive storefront or screenshot check is claimed. -- Repository-wide strict Swift formatting, Chinese localization, whitespace, and project plist checks passed. - All three changes total **211 fewer production Swift lines**, excluding added tests/docs and the unrelated - worktree deletion of `.TermindPresentationQA.xcodeproj`. Full-suite results and their limitations follow below. - -### Final verification — full-suite gate remains blocked - -- The final pinned full actions built successfully and reported **macOS 1,527/1,530** and **iOS 528/529**, with - no skipped tests and unchanged source snapshots throughout both actions. All three new shared suites passed - on both platforms. Full evidence: `/tmp/termind-refactor-three.7kUsqP/final-*`. -- Three failures repeat the immutable initial baseline: `AgentApprovalQueueTests.cancellingAParallelBatchStillAnswersEveryCall`, - `ServerMetricsLifecycleTests.oneWorkerSurvivesWindowsAndStopsOnPauseSleepAndRemoval`, and - `MobileKnowledgeToolRenderingTests.knowledgeRowsStayCompactAtPhoneWidthAndExpandOnDemand`. - The initial `AgentRuntimeTests.stoppingARunCancelsTheToolsItIsWaitingOn` failure did not recur; no fix is claimed. -- The final macOS run also failed `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable`. - This did not fail in this pass's initial baseline, although three earlier entries record the same assertion. - Both its production source and test are byte-identical to `HEAD`. A minimal Swift package containing only - those two unchanged HEAD files passed all two tests in three independent processes; that smaller harness - is not equivalent to the complete Xcode target. Current Xcode isolation reproduced the macOS assertion - (1/2 passed), while iOS passed 2/2. Evidence: `waveform-baseline-*`, `waveform-current-*`, and the source manifest - under `/tmp/termind-refactor-three.7kUsqP/`. -- A clean export of `8509ddd5` was then built with pinned dependencies and a separate DerivedData directory. - A framework-only diagnostic scheme passed 2/2, but the **original TFM scheme reproduced the same waveform - assertion (1/2 passed)** with unchanged baseline sources and without any of these refactors. Evidence: - `/tmp/termind-refactor-three.7kUsqP/waveform-head-tfm.*`; the passing framework-only comparison is - `waveform-head-xcode.*`. The failure is sensitive to the scheme/harness; its underlying cause was not repaired - or fully diagnosed. All four final failing cases now have pre-change evidence; a smaller harness alone - would have been insufficient to establish that. -- No unrelated failing implementation/test, AgentKit source, package pin, or user's QA-project deletion was - changed to force a green result. The three requested implementations have passing affected checks, but this - is not an all-green full-suite result and the outstanding test gate remains unresolved. -## Width-based mobile layouts (2026-09-18) - -- **Evidence:** `rg 'mobileInterfaceIdiom|MobileInterfaceIdiom' TFI TFITests` found - device-based navigation, sheets and cards, plus a duplicated workspace test shell. -- **Action:** remove the device environment and injection; use the real root in - workspace tests. Native compact and regular shells may rebuild at width-class changes. -- **Estimated reduction:** about 50 lines from the obsolete environment and test shell, - excluding the new width transition behavior and regression coverage. -- **Status:** implemented and verified within the simulator coverage below; the - full-suite gate remains non-green because of confirmed baseline failures. -- **Verification:** real-root tests cover repeated native sidebar/tab reconstruction, - actual sidebar selection, Settings depth reset, discarded unsaved forms, Metrics - list mapping and Back, retained Assistant drafts, independent window gates, - deferred successful/failed saves, and existing SSH/SFTP connections. English, - Simplified Chinese and Traditional Chinese snapshots are attached to the results. - iPhone focused checks passed 25 tests; the final breadcrumb adjustment passed - all four workspace rendering tests again on both iPhone and iPad. Actual - portrait/landscape navigation was inspected on both simulators. Device Hub did - not drive the system window-corner resize gesture: width changes and two-window - independence were verified with real roots using frame and trait changes, - rather than a manual Stage Manager drag. Formatting, Chinese localization and - whitespace checks pass. Dependencies remain pinned and unchanged. -- **Full runs:** iPad reported 537 passed, 3 failed, 0 skipped out of 540; - macOS reported 1,527 passed, 1 failed, 13 skipped out of 1,541. All new tests - passed. Results: `/tmp/termind-width-final-ipad.xcresult`, - `/tmp/termind-width-rebuild-macos.xcresult`, `/tmp/termind-width-iphone.xcresult`, - `/tmp/termind-width-path-final.xcresult`, and - `/tmp/termind-width-path-final-ipad.xcresult`. An earlier mobile run interrupted - during cleanup of an older test process was superseded by the complete iPad run. -- **Baseline failures:** a clean export of `c015d5f3` with the original TFI scheme - reproduces the iPad tab-bar visibility, host-card height and knowledge-row - expansion assertions. The same baseline card suite passes on iPhone. Evidence: - `/tmp/termind-width-baseline-ipad.xcresult` and - `/tmp/termind-width-baseline-cards.xcresult`. No unrelated code or assertion was - changed to hide these failures. The macOS full run has the previously documented - `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable` - failure; its source and test remain unchanged. - -## Consolidate Main Window Registration and Content Style (2026-09-18) - -- Evidence: `rg` found `RootView` mounting both `MainWindowRegistration` and - `FullHeightWindowContent`; the latter existed only to set two native window - properties. Separate attachment callbacks could size a window before its - full-size content coordinate system was established. -- Action: set the style and titlebar transparency in the existing registration - bridge before registering, and remove the redundant representable and NSView. - Both operations remain in the macOS platform layer. -- Reduction: remove 30 lines of bridge plumbing, adding 5 lines at registration. -- Status: completed. All 47 focused window, lifecycle, and terminal-shortcut tests - passed. Strict Swift formatting, localization validation, and diff checks passed. -- Full pinned macOS verification used the immutable `94ad70f1` snapshot because - concurrent Dock-menu work introduced a separate test compilation error into - the shared checkout. Result: 1,593 passed, 8 skipped, 5 failed. Failures were - `StoreKitTrialTests.annualProductSuppliesPriceBillingPeriodTrialDurationAndEntitlement` - (JPY storefront values instead of the test's USD values), - `AgentApprovalQueueTests.cancellingAParallelBatchStillAnswersEveryCall`, - `ServerMetricsLifecycleTests.oneWorkerSurvivesWindowsAndStopsOnPauseSleepAndRemoval`, - `MetricsJourneyTests.offlineRestoredMetricsReconcileOnMainActor`, and - `TerminalCommandDraftControllerTests.readsOneDraftAndRejectsConcurrentRequests`. - The Agent and server-worker failures have earlier baseline evidence above; - no baseline claim is made for the other three. No unrelated implementation or - assertion was changed. Evidence: `/tmp/termind-window-sizing-focused-fixed.xcresult` - and `/tmp/termind-window-sizing-snapshot-full.xcresult`. -- Native app checks confirmed a 904-by-572 window creates another of exactly the - same size, fullscreen Command-N opens Connect, and leaving fullscreen retains - the original size. Close/reopen preserved 904 by 572; isolated quit/relaunch - preserved 1004 by 652. Quitting with differently sized windows and reopening - used the last-used 904-by-572 size. The current macOS setting reopened one scene; - preserving distinct restored frames is covered by the native-window tests. - A concurrently running same-bundle demo initially interfered with system frame - restoration; the relaunch checks were repeated without another instance. - -## Resolve Metrics Clock and Short Knowledge-Card Baseline Failures (2026-09-18) - -- The Metrics lifecycle test advanced its manual clock after the first base point, - before optional GPU collection and the sampler's next sleep had necessarily - finished registering. The pre-fix repeated run failed 3 of 5 iterations. A - controlled probe of the existing model reproduced the missed advance; another - 0.05 seconds of simulated time released the sampler. The test now waits for - both maintenance and sampling sleeps, and its polling helper preserves the - caller's source location. Production Metrics lifecycle code is unchanged. -- The SwiftUI compact detail's flexible maximum height accepted the test's large - height proposal even for short content. A trailing vertical `fixedSize` now - preserves natural height within the scaled preview cap. The short installation - fixture measures 99.33 points instead of occupying 180 points on the iPhone - simulator. Tests classify the fixtures by preview truncation rather than tool - name: short details must match natural height, while long rows must expand, - retain expansion across layout, and return to compact height when collapsed. -- Focused macOS lifecycle/knowledge checks passed all 26 test functions; the - lifecycle case additionally passed 20 of 20 repetitions. All three mobile - knowledge tests passed, covering English, Simplified Chinese, Traditional - Chinese, phone width, and accessibility text size. Native macOS row checks - retain their 380- and 720-point widths. Strict Swift formatting, Chinese - localization validation, and whitespace checks passed. -- Full pinned results: macOS **1,600 passed, 1 failed, 8 skipped / 1,609**; - iOS **584 passed, 3 failed, 0 skipped / 587**. The repaired lifecycle and all - knowledge rendering tests passed in these full actions. The macOS failure was - the previously recorded - `AgentApprovalQueueTests.cancellingAParallelBatchStillAnswersEveryCall`. - Mobile failures were - `MobileUploadBatchTests.sftpFolderUploadUsesTheCurrentDirectoryAndOneBatch`, - `MobileEditorToolbarTests.cancelOnlyDismissesUnmodifiedDrafts(isDirty:)`, and - `MobileAdaptiveLayoutTests.settingsDepthAndUnsavedFormsResetOnlyAcrossWidthClasses`. - All three mobile cases passed focused reruns. This is not an all-green full - suite, and those reruns do not establish a baseline or fix for unrelated cases. -- Evidence: `/tmp/termind-metrics-knowledge-fix-{macos-focused,repeat20,ios-focused,macos-full,ios-full}.xcresult` - and the matching logs; mobile reruns are `ios-unrelated-rerun` (editor/settings) - and `ios-upload-rerun` under the same prefix. The full mobile action completed - both test bundles before its slow optional `simctl diagnose` collection was - interrupted; its finalized result retains all 587 tests and the three failures. - Package pins, public interfaces, and persistence were unchanged. - -## Preserve Mobile Navigation During Window Resizing (2026-09-18) - -- Evidence: `rg 'layoutTransition|compactMetrics|registrations\(on|scene\.layout' TFI TFITests` - found two root navigation trees, width-triggered route clearing, duplicated Metrics - presentation, and a save gate whose sole purpose was delaying reconstruction. -- Action: use one native sidebar-adaptable TabView with five stable feature stacks; - remove width-specific registration, route migration, and reconstruction save gating. - Keep Metrics search shortcuts directed at its single top-level destination. -- Estimated reduction: 150-200 production lines, excluding replacement lifecycle and - narrow-window label coverage. Preserve sheet save/cancel/error behavior and native - terminal ownership. -- Status: implemented, reducing production code by 210 lines. Metrics navigation and - Vault search use the desktop `waveform.path.ecg` symbol. -- Verification: the focused iPad action passed all 21 test functions across adaptive - navigation, scene/session ownership, editor saves and feature ordering. The same - iPhone action passed 20 of 21; its scroll fixture incorrectly forced a regular - size class onto a compact-only phone, changing native search-bar placement. The - corrected fixture uses the device's native class plus compact widths; it passed - three repetitions each on iPhone and iPad, retaining the iPad regular/compact - transitions. State checks cover selected pages, Settings depth, unsaved forms, - search/scroll, Assistant drafts, independent windows, saving/error feedback and - existing SSH/SFTP instances without reconnecting. -- Reviewed English, Simplified Chinese and Traditional Chinese screenshots at - normal and accessibility text sizes. All five titles fit the 320/375-point bottom - bar. The regular iPad top bar scrolls at intermediate widths; entries are retained - and remain selectable. Tests resize the hosting window as well as its child; - native title scaling is not treated as truncation by comparing intrinsic widths. - Manual iPad rotation retained the Models child page and showed the unified icon. - Device Hub did not perform the system corner-resize gesture; repeated window - resizing remains covered by native window-frame/trait tests. -- Full results before the fixture corrections: iPad **583 passed, 3 failed / 586**; - iPhone **584 passed, 2 failed / 586**; macOS **1,598 passed, 1 failed, - 8 skipped / 1,607**. New failures were the corrected tab-label/scroll fixtures. - The other failures were the previously reproduced iPad tab-bar visibility and - host-card height cases, and the previously recorded macOS waveform bitmap case. - Their production code and assertions were not changed for this work. These are - not all-green full-suite results. -- Evidence: `/tmp/termind-adaptive-tabs-{ipad,iphone,macos}-full.xcresult`, - `/tmp/termind-adaptive-tabs-final-{ipad,iphone}.xcresult`, and - `/tmp/termind-adaptive-tabs-scroll-native-{iphone,ipad}.xcresult`. Strict Swift - formatting, Chinese localization and whitespace checks passed. Package pins, - public interfaces and persistence remain unchanged. - -## Remove Desktop Window and Quit Confirmations (2026-09-18) - -- Evidence: `rg 'dismissalConfirmationDialog|Quit with unuploaded changes|sftpEdits.recheck' TFM` - found the Workspace-based window dismissal dialog and a separate SFTP edit check - used only to present the quit warning. Five catalog entries belong only to these dialogs. -- Action: remove both confirmation paths and their unused copy while preserving native - window dismissal, pane-first Command-W, per-window session ownership, and bounded quit cleanup. -- Estimated reduction: 25-35 production Swift lines and five localized catalog entries. -- Status: superseded by the user's request to retain both confirmations and hide - only the close-button edited indicator. Both dialogs and all five strings are restored. - -## Hide the Desktop Close-Button Edited Indicator (2026-09-18) - -- Evidence: the restored SwiftUI WindowGroup sets `NSWindow.isDocumentEdited` when - a Workspace requires dismissal confirmation. An isolated Demo probe cleared that - property and still presented the original Close Window / Cancel dialog. -- Action: observe the native edited flag only while the main-window registration - view is attached and clear the decoration. Retain SwiftUI's dismissal delegate, - Workspace condition, all confirmation copy, and SFTP quit checks. -- This is a visual behavior correction, not a code-reduction refactor. No shared - API, persistence, package, or dependency change is needed. -- Status: implemented; verification pending. - -## Make Workspace Composition Free (2026-09-18) - -- Evidence: `rg 'workspaceComposition|window.proAccess' TFM Frameworks` found - 15 composition gates, a window-only commerce dependency, and a launch-scoped grant. -- Action: remove composition from Pro, delete its gates and sticky authorization state, - and align app, website, StoreKit copy, and architecture documentation. -- Estimated reduction: 35-45 production Swift lines plus unused localized entries. -- Status: implemented; removed 34 production Swift lines and two unused localized entries. -- Verification: all 28 Workspace access cases across seven non-Pro states, all six - remaining Pro feature gates, and entitlement assertions passed. Strict Swift formatting, - Chinese localization, website build, and whitespace checks passed. Visually reviewed - English, Simplified Chinese, and Traditional Chinese home/FAQ content at desktop and - mobile widths; both legacy Chinese redirects passed. -- Full macOS and iOS tests were stopped at the user's request. Before cancellation, - the Mac run reported failures in AgentBackgroundCommandTests, AgentApprovalQueueTests, - and SpeechWaveformViewTests; these were not investigated or rerun. No all-green - full-suite result is claimed. App Store Connect metadata remains a release step. - -## Share Desktop SSH and SFTP Authentication Submission (2026-09-19) - -- Evidence: `rg -n 'continueAuthentication|submittedPassword|let existing = store.identity|passwordUpdate' TFM/Features/Terminal/SSHSessionModel.swift TFM/Features/SFTP/SFTPSessionModel.swift` - confirms duplicated saved-password fallback, submission construction, selected - Identity updates, custom Identity reuse, and Host association in the two desktop - authentication continuations. -- Action: share submission resolution and optional saving through the existing - Application `RemoteSessionHostStore` port. Keep username validation, passphrase - continuation, prompt/error presentation, ephemeral SFTP credentials, transient - SSH Hosts, and reconnection sequencing in their current session owners. Preserve - separate Identity and Host writes and the existing secret-access bookkeeping. - Mobile, Metrics, and Forwarding authentication retain their distinct save rules. -- Estimated reduction: 40-80 production Swift lines, including helper and caller - changes but excluding regression tests. No package, schema, or localization change - is needed. The existing localization-catalog edit is outside this refactor. -- Status: implemented, reducing production Swift by 47 lines. Both sessions reuse - two methods on the existing port; no new model, generic base type, dependency, - schema, or secret-storage implementation is introduced. Architecture documentation - records this ownership; the existing boundary tests need no rule changes. -- Focused verification: all 83 test functions passed (105 expanded test cases), - including the new shared-policy and desktop entry-point suites, credential usage, - connection preparation, Identity credentials, Connection Journey, SFTP sessions, - multi-window lifetimes, and architecture boundaries. The initial build found an - ambiguous `Host` import in the new desktop test fixture; an explicit Domain import - fixed it before the passing run. Strict Swift formatting, Chinese localization and - whitespace validation passed. Full pinned macOS and iOS verification is pending. -- Evidence: `/tmp/termind-auth-refactor.uUFmoB/focused-macos-2.xcresult` and matching - logs/summary. Production-line counts and localization/dependency fingerprints are - recorded in the same directory. Existing catalog edits remain untouched. - -## Share MCP Transfer Retention Policy (2026-09-19) - -- Evidence: fresh `rg` confirms R6's duplicated activity, waiting/paused expiry, - unfinished capacity, completion-age pruning, and oldest-first completion cap in - `MCPUploadCoordinator` and `MCPDownloadCoordinator`. Request path/digest validation - remains direction-owned; both directions use the upload-owned byte limit. -- Action: share ticket metadata, expiry construction, retention, and byte limits inside the desktop MCP layer. - Preserve per-direction dictionaries and errors, live-state definitions, Range after - success, upload cleanup, revocation, and the distinct cancellation/task ordering. -- Estimated reduction: 10-40 production Swift lines after the narrow helper pays for - its protocol surface; regression coverage is counted separately. -- Status: implementing. At the user's request, finish both remaining refactors before - running unified verification rather than testing each intermediate change. - -## Share CloudKit Coordinator Lifecycle (2026-09-19) - -- Evidence: fresh `rg` confirms R5's repeated operation generation, account checks, - engine setup, fetch-before-send gate, pause/retry, account/zone/environment resets, - completion status, and error classification in all three Cloud coordinators. -- Action: compose one independent Infrastructure lifecycle instance per coordinator. - Use a small journal-state value and closures to adapt existing typed journals, and - an injectable account/engine transport for deterministic lifecycle tests. Keep - codecs, merges, quarantine, version acknowledgements, and Settings secret handling - in category adapters. Do not alter persistent model shapes or package dependencies. -- Estimated reduction: 200-350 production Swift lines; adapter and driver costs are - included, while regression tests are counted separately. -- Status: implementing; unified verification follows all edits, as requested. - -## Centralize Responsive Mobile Toolbar Decisions (2026-09-19) - -- Evidence: `rg -n 'tabBarPlacement|mobileToolbarLayout' TFI` finds direct placement - fallbacks in Assistant and Metrics alongside the shared environment already - provided by each stable feature stack. The production UI contains no device-idiom - or model-based layout branches. -- Action: remove both feature-local placement reads and use the shared toolbar - layout as the sole policy input. Retain container geometry, active reserved regions, - native view identity, and OS availability checks at the SwiftUI API boundary. -- Estimated reduction: two production Swift lines and two duplicated conditions. -- Status: implementing; verify native title/menu transitions and fold/session lifetime tests. +English-only durable history and redundancy backlog. For future work: confirm references with `rg`, +record evidence/action/estimated net production reduction, implement one independent change, run +risk-appropriate checks, and append status/verification. Retain completed items and native lifecycle +boundaries. Structural changes must explicitly disclaim reduction. Test additions are counted separately. +Historical results below describe their recorded runs, not current verification; pending work stays pending. +“Pinned” means `-onlyUsePackageVersionsFromResolvedFile`. Formatting/localization/diff checks mean strict recursive +Swift formatting, `Scripts/check-zh-localization.sh` and `git diff --check`; Xcode validates the app module graph. + +## Completed items, 2026-08-30 to 2026-09-04 + +Items 1–32 and 34 date to August 30; later dates are explicit. “Build” means the recorded app build, +not a full test pass. Each item retains its original ID. + +1. **Legacy SwiftUI transcript:** removed unreachable transcript/message/command/compaction/plan trees; + retained live timestamps/risk components. Build and targeted Transcript tests passed. +2. **List hosting:** shared `HostingListRow`/`HostingListContent`; retained Transcript/conversation lifecycles. + Build passed before the final parser-verified Connection Log migration. +3. **SSH initializers:** one private initializer, preserving temporary Hosts. Build passed. +4. **Terminal appearance:** SSH/Serial reuse `TerminalViewState.applyAppearance`. Build passed. +5. **SFTP store overloads:** removed four `TransferLibraryModel` forwarders for `RemoteSessionHostStore`. Build passed. +6. **Jump Host builder:** reused `sshJumpConfigurations`, preserving validation/errors. Build passed. +7. **Connection interaction:** shared trust/progress/prompt/reconnect policy in `RemoteSessionConnectionController`, + keeping transport teardown local. Debug build and connection/SFTP tests passed. +8. **Tool-free text collectors:** shared streamed text/tool rejection for commands/titles/compaction. Focused tests passed. +9. **Provider authorization:** shared auth/ephemeral sessions; retained Vertex/empty-secret rules. Provider/ChatProbe passed. +10. **SwiftData transactions:** narrow `ModelContext.commit`, retaining two-stage history commits. Connection/SFTP History passed. +11. **libssh2 continuations:** shared identical SSH Exec/SFTP serial-worker bridges. Build passed. +12. **Legacy Transcript helpers:** removed dead streaming/edit-binding/activity helpers with item 1. Transcript checks passed. +13. **Composer migration:** callers use `conversation.composer`; removed four accessors, retaining attachment hooks. + Build and Chat Composer tests passed. +14. **Toolbar search:** deleted unreachable menu/delegate/find-kind plumbing; query/keyboard behavior retained. Build passed. +15. **Agent SSH prompts:** `AgentSSHAuthenticationRequest` supplies both Journey/passphrase projections. Build passed. +16. **Atomic SFTP writes:** shared temp paths/cleanup/unlink/rename/progress, keeping source loops separate. Build/SFTP passed. +17. **Transfer conflicts:** shared Keep Both/directory/file policy, retaining distinct jobs. Debug build/SFTP passed. +18. **Client leases:** shared saved/borrowed acquisition and conditional disconnect; reservation state retained. Build/SFTP passed. +19. **Legacy tool metadata:** one compatibility table, retaining generic MCP fallback. Presentation tests passed. +20. **Test-only presentation actions:** removed unused automatic messages/verb duplication around `actionKind`. Presentation passed. +21. **Tool input unpacking:** narrow `AgentToolDetailFormatting` object/array helpers, local field presenters. Build passed. +22. **Idle sweepers:** one `AgentIdleSweeper`, caller-owned registries/emptiness. Focused tests passed. +23. **Sidebar headers:** reused `SidebarSectionHeader` for count/collapse instead of Assistant/Snippet copies. Build passed. +24. **Host deletion:** groups use repository/outbox saga; deleted direct snapshot deletion. Debug/repository/Snippet/forwarding passed. +25. **Unused declarations:** reference-confirmed Agent preflight/settings/metrics/reconnect/SSH-status wrappers/constants removed. Build passed. +26. **Settings page:** two callers use identical `SettingsFormPage`; deleted `SettingsCollectionPage`. Build/Settings passed. +27. **Cursor lists:** shared fixed-height Snippet Target/Host Chooser/Export Host adapter; specialized rows excluded. Build/list tests passed. +28. **Agent authentication:** shared typed Exec/SFTP authenticated-open retry loop. Build and all 21 Host Authentication tests passed. +29. **Transcript risk/confirmation:** shared native-row risk color/destructive confirmation. Build passed. +30. **Glass shapes:** one private shape-parameterized modifier, unchanged APIs/fallbacks. Build passed. +31. **Vault envelopes:** shared insert/update/upload indexing; callers retain versions/merge rules. Build/Vault Sync passed. +32. **Remote preflight:** shared directory ordering/descendant skip/target-kind lookup; prepared jobs remain distinct. Build/SFTP passed. +33. **Vertex selection (09-04):** `ModelProvider.usesVertexEndpoint`/`effectiveCredentialRef`, later adapted to API formats. + Build/Provider checks passed. +34. **Dead fields:** removed unused onboarding/Agent/color/trial/runtime Linux metadata, retaining generated JSON provenance. Build passed. +35. **Run presentation (09-01):** derive state from reported run/outstanding queues. State/localization/diff checks passed. +36. **Copy-only tests (09-02):** removed 20 tests/literals; kept behavioral/persistence/language/protocol coverage. + Target compiled; focused tests passed. +37. **Provider HTTP (09-02):** shared transport/HTTP/non-2xx validation; requests/parsing local. Build and 12 tests passed. +38. **Finder drop wrapper (09-02):** deleted unconstructed `NativeFileURLDropTarget`, retaining general target/test initializer. + Build and five drop tests passed. +39. **Context Chip recovery (09-02):** shared payload boundaries/fallback filenames for live/restored attachments. Build/recovery passed. +40. **Save/rollback reuse (09-02):** `savePendingChanges`/`commit` cover Agent/Outbox/AI/conversation without shifting commits. + All 46 focused tests passed. +41. **Overlay chrome (09-02):** three modifier chains use `termindTransientOverlaySurface`; content/transitions local. Debug passed. +42. **SSH form chrome (09-02):** shared `connectionFormChrome`, distinct form behavior. Formatting/Debug passed. +43. **Snippet content (09-02):** `SnippetCardContent` shared with drag ghost, separate interaction/chrome. Formatting/Debug passed. +44. **SFTP reads (09-02):** inline/two download paths use `withRemoteReadHandle`, keeping loops/destinations. Build/47 tests passed. +45. **Chooser selection (09-02):** shared `ManagedChooserState.allChecked(in:)`/`toggleAll(in:)` for three equal eligibility sets; + restricted selection retained. Build/localization/diff passed. +46. **AgentKit extraction (09-02):** moved portable runtime behind host/interaction/prompt/presentation seams. + Package build/tests/formatting and app build/full suite passed. +47. **Workspace commands (09-02):** removed AppKit-shadowed commands, retained standard shortcuts and moved split bindings + off system keys with terminal routing coverage. Build/formatting/localization passed. +48. **Provider addressing (09-04):** one URL plus `ModelAPIFormat` replaces vendor/default-path branching; safe migration, + AgentKit 0.4.0/0.5.0. Package checks, 994 app tests, localization/site build passed. + +## Later completed work, 2026-09-09 to 2026-09-14 + +| Date | Change and retained boundary | Recorded verification / reduction | +| --- | --- | --- | +| 09-09 | StoreKit replaces local HMAC/Keychain/CloudKit trial with transaction clock watermark. | −121 production lines; commerce passed except unrelated catalog expectation. | +| 09-09 | Delete unread Show Recents setting/UI/labels; retain Sidebar accessibility localization. | Builds, formatting, localization and relevant tests passed. | +| 09-11 | Remove five redundant tests and literal/enum/catalog snapshots; retain behavior. | −77 test lines; relevant checks passed, unchanged intermittent focus test passed rerun. | +| 09-11 | Delete `ScreenshotDemoWindow`; use production WindowGroup with demo restoration off. | Full pinned tests, native capture, formatting/localization passed. | +| 09-11 | Share SFTP permission Domain draft/Presentation grid. | Regression evidence was recorded in `MOBILE_ROLLOUT.md`. | +| 09-11 | Move CloudKit/settings transactions/journals/terminal/history to frameworks; retain desktop adapters. | Shared/mobile passed; full macOS retained known Agent timing failures. | +| 09-11 | Application owns Metrics/Journey state, Presentation panels/forms; native keyboard/logs stay local. | Shared/mobile and 127 focused macOS tests passed. | +| 09-11 | Share skill library/importer/role form in Application/Infrastructure/Presentation; native editors retained. | Isolated build and 81 mobile/shared tests passed. | +| 09-12 | Share Assistant runtime/tools/attachments/speech/conversation/list behavior. | Builds/navigation/Assistant/localization/formatting passed; unrelated mobile Metrics race passed rerun. | +| 09-12 | Remove app forwarding aliases; retain six responsibility-named adapters. | −86 files, 224 app aliases, 20 Persistence aliases; both builds/architecture/full TFI passed. | +| 09-12 | Align TFI layers, move connection construction to Infrastructure, centralize then-current idiom policy. | Both builds/architecture/full TFI passed; later width work supersedes idiom policy. | +| 09-12 | Share `TerminalCommandDraftController`/`CommandGenerationCoordinator`, timeout/cancellation/protocol constants. | Focused desktop/mobile passed. | +| 09-12 | Domain `RemoteFile`, Application `RemoteFileSystem`/`MobileSFTPSessionModel`. | Structural net increase; builds/architecture/SFTP passed. | +| 09-12 | Mobile adopts shared trust/progress controller and Workspace/Metrics temporary-key preparation. | Connection/Journey/Metrics/recovery passed. | +| 09-12 | Share `TerminalFindController`; platform binding closures retained. | Direct/platform tests passed. | +| 09-12 | Domain detects Linux distribution once, distinguishing unavailable/conclusive. | Domain/Host/mobile Workspace passed. | +| 09-12 | Share Journey header/status; native logs/failures/backgrounds/keyboard retained. | −98 production lines; builds/Journey passed. | +| 09-12 | Presentation owns Ghostty appearance conversion; desktop preference storage retained. | Both builds/themes passed; dependencies unchanged. | +| 09-12 | Share terminal-upload readiness/notices, distinct platform execution. | Direct policy tests/both builds passed. | +| 09-12 | Share hashed/endpoint Known Host titles, distinct cards. | Direct presentation/both builds passed. | +| 09-12 | Restrict ListViewKit: remove eight macOS adapters/eleven mounts, retain narrow UIKit transcript/history uses. | −611 macOS lines; builds/focused/format/localization/boundaries passed. | +| 09-12 | Share transcript tools/results/disclosure/timestamps/Markdown, retaining native renderers/viewport ownership. | Both builds/architecture record verify boundary. | +| 09-12 | Remove Periphery-confirmed 34 imports/three empty files/declarations; retain dynamic/protocol entry points. | −150 production lines; builds/focused/format/localization/diff passed. | +| 09-12 | Remove concrete native/list implementation-type assertions. | Behavioral rendering/identity/reuse/interaction/environment coverage retained. | +| 09-13 | Remove 81 compiler-confirmed unused localizations/three obsolete expectations/verified stale metadata. | Both builds and localization/language checks passed. | +| 09-13 | Use `ChatSlashCommandRegistry` with explicit platform ownership. | Both builds and registry/desktop/mobile tests passed. | +| 09-13 | Inject app-owned `MobileFeatureRegistry`; replace tab/sidebar/destination copies. | iOS build and 28 navigation tests passed. | +| 09-13 | Share tree/route/deletion/display/Snippet/model/SFTP-drag/service policy; retain native lifetimes. | Both builds/shared/mobile passed. | +| 09-13 | Annual introductory offers replace new standalone trials; legacy entitlement temporarily retained. | Offer/eligibility/duration/price/period/expiration derive from StoreKit; legacy removal follows below. | +| 09-13 | Remove 27 mobile/seven app source snapshots, host-state Keychain/status integration, duplicate permission/history tests; compatibility/defaults/limits move to FrameworkTests. | −1,303 lines; 1,079 macOS, 13 shared, 218 iOS tests plus formatting/localization/diff passed. Existing waveform bitmap failure remains outside cleanup. | +| 09-14 | Fresh scan removes standalone-trial ID/domain/clock/entitlements/product/UI/tests/copy; `lifetimeProductID` and annual trials remain. | Estimate −90 production lines; format/localization/site, 1,091 macOS, rerun Metrics and 187 shared iOS passed. iOS built; ten unrelated toolbar tests failed full/isolated runs. | + +## Retained boundaries + +Native transcript/conversation/dynamic/drag lists retain dedicated lifecycles. Share only portable SSH/SFTP/Serial/ +platform policy; differing startup, credential saves, teardown, logs and interaction stay separate. Frozen SwiftData +schemas are migration contracts; small lookalike repositories/views need no abstraction without reduction. +`RuntimeVersions` stays platform-local to avoid invalid Ghostty dependencies. The 09-02 completion/recheck found +no further qualifying independent duplication/dead code; every later candidate needs fresh references. + +## Planner reuse and test cleanup, 2026-09-16 + +### Desktop download planner — completed + +- Evidence: `rg 'PreparedJob|PreparedBatch|pendingBatch|keepBothLocalURL' TFM TFI Frameworks TFMTests TFITests` + found desktop's sole mixed batch path was download-only, with empty remote-directory collections; mobile already + used `SFTPRemoteDownloadPlanner` and uploads `SFTPLocalUploadPlanner`. +- Adopt shared traversal/local conflicts/scoped destination/enqueueing; retain desktop pending/errors/copy policy/ + lifetimes. Destination ownership now creates directory-only trees. Estimate −180, actual −167 production lines + (−59 including regression tests); architecture updated. +- All 75 SFTP/architecture tests passed, including three new tests/ten conflict/subtree/empty/cancellation cases; + empty-directory regression fails with old production code. Full pinned macOS: 1,337/1,343. Four failures reproduce + before change: `SettingsSyncTests.providedDatabaseCopyMigratesWithoutReset` (schema 15 vs 17), + `ModelCapabilityResolverTests.reportedFieldsWinOverCodexAutoReviewOverride`, + `ScrubberWebClientTests.fetchPreflightNeverEscalatesPrivateHTTP`, + `ChatComposerStateTests.aFailureNoticeIsNotAnAnswerWorthNaming`. + `MetricsJourneyTests.offlineRestoredMetricsReconcileOnMainActor` passed rerun; + `SSHImportTests.indexedIdentityPlanningHandlesTheImportFileLimit` exceeded two seconds concurrently but passed + isolated against both versions (final 0.100 s). +- Full pinned iPhone 17 Pro/iOS 26.5: 426/432, all 205 framework tests passed. Five Assistant/import failures remained + on rerun (identifiers below); `MobileEditorToolbarTests.iconButtonsKeepTheirNamesAndSaveLifecycle` passed rerun. + Formatting/localization/symbol/diff checks + passed; no dependency/catalog/shared production changes. + +### Desktop copy planner and duplicate assertions — implemented, partial verification + +- Evidence: `rg 'PreparedRemoteCopy|pendingRemoteCopyBatch|SFTPRemoteCopyPlanner.enqueue|resolveRemoteDirectories'` + across app/framework/test Swift found desktop-only preparation/conflict/enqueue copies. Adopt the existing mobile + planner and directory-mutation reporting, preserving pending/errors/completion refresh/interactive deletion. + Estimate −220, actual −193 production lines; coverage retains validated recursive deletion/cancellation and adds + three-policy file/directory/subtree conflicts, empty refresh and pending cancellation. Upload helpers remained R2. +- Reference scans of role-label, unknown-command, approval-copy, handshake-description and upload-notice tests + removed pure literals/duplicates, keeping parameterized classification, endpoint/destructive policy, locales, + diagnostics and gating/payload behavior. Estimate −50, actual −59 test lines; +75 copy regression lines gives + −177 total Swift. Architecture/language/catalog/protocol/round-trip/behavioral string assertions remain. +- Focused pinned TFM: 200/200; full: 1,436/1,437, unchanged + `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable` bitmap assertion. Earlier records + report it, but this pass ran no baseline/isolated rerun. TFI never built/tested: unavailable destination UUID; + owner requested commit before retry after discovering a booted phone. Not a passing iOS result. +- Formatting/localization/removed-symbol/diff checks passed; Xcode resolved modules standalone SourceKit could not. + Existing SwiftLint size/complexity findings stayed outside scope, without suppressions. Dependencies/catalogs unchanged. + Evidence: `/tmp/termind-refactor-copy-{focused,macos}-20260916.xcresult`. + +### Low-value UI/source assertions — implemented, full verification recorded as in progress + +`rg '#filePath|sourceRoot|String\(contentsOf|source\.contains' FrameworkTests TFMTests TFITests` found 35 architecture +and eight UI/source snapshots. Removed implementation/copy/layout/enum/palette/fixed-canvas/disconnected sorting +checks and orphan helpers: 52 declarations/1,230 lines versus estimate 1,000–1,200. Source checks fell 43→9 narrow +ownership/dependency guards; native-list allowlist permits importer removal but rejects unapproved additions. +Grouped Host tests now use fixture placement. Behavioral/language/protocol/opt-in screenshots remain; production, +packages/catalogs unchanged and existing architecture edits preserved. Initial pinned macOS 18/18, iOS 5/5; +format/localization/remaining-source/diff checks passed. Standalone SourceKit partly inconclusive; generic SwiftLint +size/tuple/pixel-name/force-try findings untouched. Full runs were proceeding sequentially; no final pass recorded +here. Evidence: `/tmp/termind-test-cleanup-initial-{macos,ios}.xcresult`. + +### SFTP status labels — verified with existing iOS failures + +`rg 'status\(for: transfer\)|transfer\.state\.label' TFM TFI` found mobile direction mapping/two generic desktop +labels. Presentation now supplies shared running/completed direction labels, generic other states: −8 production +lines as estimated, unchanged translations/transports. Pinned macOS 1,183 app +213 shared passed; iOS 212/217 app ++213 shared, with the same five Assistant/import failures/text as `/tmp/termind-test-cleanup-full-ios.log`. +Formatting/localization/diff passed; English/zh-Hans/zh-Hant resource-backed label matrices reviewed, not live +transfers. Evidence: `/tmp/termind-sftp-status-{macos,ios}-20260916.xcresult`, `/tmp/termind-sftp-status-preview/`. + +### Desktop forwarding adapter — implemented; verification blocker later resolved by the 09-17 repair + +- `rg 'PortForwardingEditorState|PortForwardingEditorPolicy'` across sources/tests/Memory/project found a 43-line + adapter serving three computed desktop getters/seven tests; mobile already used shared policy. Replace with + `PortForwardingEditorPolicy`, preserving synchronous observed saved-rule/Host/controller reads, callbacks, + intent OR activity, normalization, draft Host-ID lookup, edit locks and native runtime values. No new wrapper/API/ + schema/secret/approval/dependency/localization change. Estimate −27–28; actual −27 production (−43/+16), + +109 test lines, +82 total. Seven desktop behaviors retained; shared matrix adds 14 cases including hidden dynamic + targets, local/remote normalization, activity/intent and missing targets. Independent reviews/audit found no issue. +- Focused pinned TFM 33/33 (14 feature, seven editor, nine architecture, three shared functions/14 cases), re-extracted + without rerunning: `/tmp/termind-forwarding-adapter-verify.xMtOCK/focused.xcresult`. + Full macOS 1,399/1,399 (1,183+216), no skips; iOS 428/433 (212/217+216), five failures/no skips, exact rerun 0/5. + Simulator: iPhone 17 Pro/iOS 26.5, `38789AE8-B5AA-46CA-A15C-77CE760FC550`. +- Repeated iOS identifiers: `MobileAssistantTests.attachmentOnlyFirstSendPromotesTheConversation`, + `firstNewConversationSendPromotesDraftAndStartsRun` (expected user/assistant), + `existingConversationSendStillUsesTheDraftExecutionPath` (expected user/user/assistant), + `completeRegistryIncludesRemoteAndBuiltInToolsOnlyForCapableModels` (expected no `terminal_suggest`), and + `MobileVaultFeatureTests.importRequiresAnExplicitUsernameWhenTheConfigurationOmitsIt` (`model.canImport`). + No pre-refactor baseline was run; attribution was unverified, not established regression/pre-existing behavior. + All planned stages completed after provider interruption; it was neither test failure nor user stop. +- Formatting/localization/diff/retired-symbol checks passed. No-index exit 1 was corrected as “files differ” in + `untracked-whitespace-adjudication.json`, preserving raw evidence; supplementary scan passed. Patch, including + untracked test, matched verifier/`partial.patch` byte-for-byte (SHA-256 + `f76b9950aa3bb49140865feec1153836f59ce903d25f3a32edcbf07ab320a691`); dependency hashes matched HEAD/evidence. + Audit altered documentation only, with no rebuild/source edit/commit; five paths remained at `8b01a631fefdbe14e1324d87c91d0860124d85d3`. +- Manual status/toggle/lock/save/cancel/back checks were not run; no visual claim. Full results retain 166 macOS + warnings (88 locale/74 constraint/four StoreKit listener), 29 iOS (27 locale/two view-update publishing), none in + targeted rerun; focused logs retain row-height/Xcode warnings. Warnings were not baseline-attributed/investigated. +- Evidence: `/tmp/termind-forwarding-adapter-recovery.eggzSE/remaining-checks.9wl35uwk/` contains full/rerun results, + commands/logs/reports/test trees/comparisons/simulator/patch evidence; sibling `audit-record/` has independent + re-extractions/integrity/diff results. Implementation: `/tmp/termind-forwarding-adapter-implement.aTsWRl/checks.{sh,log}`. + +## Five repeated iOS failures, 2026-09-17 — repaired and verified + +Clean pinned `d582d711` reproduced all five (0/5) before this test-only repair, establishing this repair's baseline, +not retroactively a pre-refactor baseline. Three send tests now assert user-only history, failed-run/prompt association, +failure timeline and title exclusion: `b2f05217` intentionally stopped synthesizing replies without a Chat model. +Existing-chat testing awaits failed state rather than 20 yields. Registry testing follows `df3bd04c`: stable +`terminal_suggest` definition, snapshot-gated execution, equal descriptors across surfaces and no tools for incapable +models. Import testing follows `ac165a9a`'s indexed identities: real `SSHImportReader`/temporary config/empty default +username reject invalid commits; adding `User deploy` and rescanning imports a linked Host/Identity. Obsolete fake +removed, without test-only production mutation API. + +All five identifiers remain, none skipped/deleted/expected-failing; only two test files/record changed. Three +independent actions passed all five (`fixed-repeat`, `focused-2`, `focused-3`); the first `-test-iterations 3` actually +ran Swift Testing once. Full pinned TFI 433/433, TFM 1,399/1,399, no failures/skips, resolve the preceding automated +blocker. Formatting/localization/whitespace passed; Xcode, not inconclusive SourceKit, validates modules. Full runs +retain 29 iOS/168 macOS uninvestigated/unattributed warnings, focused runs none. Evidence/scripts: +`/tmp/termind-ios-five-fixes.NfgsN4/` (`baseline`, focused actions, `full-ios`, `full-macos` results). + +## Skill packages and immutable helpers, 2026-09-17 + +- **Single-document loader — implemented.** `load_skill` duplicated definition/presenter/guidance/output exemptions. + Replace executable `LoadSkillTool` with `skill_read_file` for bounded instructions/supporting resources; retain + historical presenter/replay only. Estimate about −100 retired-loader lines; package management is additive, no + overall reduction claim. AgentKit tests passed; app skill/migration/memory/mobile suites exercised. +- **Starter runbooks — implemented.** `AgentSkillLibrary` shipped three editable seeds, duplicated in library/sync + persistence, with destructive Reset in both apps. Replace with two immutable runtime helpers; delete seeds/Reset/ + old fixtures, preserving installed starters as ordinary user records. Native conflict repair and atomic install/ + sync transaction remain. Estimate about −200 retired production lines, excluding installer/migration coverage. + Signed/published AgentKit 0.13.0: 213 package tests passed. Memory labels remain 持久化记忆 / 持久化記憶. + Final iOS 452/452, macOS app 1,183 passed, AppLanguage independently passed; refreshed shared 231/232 with existing + waveform PNG failure. New skill/library/sync tests, bilingual native review, formatting/localization/diff passed. + +## Reuse and architecture scan, 2026-09-17 + +Discovery baseline `8509ddd5`: inventory/normalized-block scan of 516 production Swift files/113,165 lines across +frameworks/apps/bridge; inspected candidate bodies/callers/tests, not every file behavior. Import audit of 273 +framework files found no prohibited edge. Stale source graph/unavailable structural navigation were not reachability +proof. Website localized home already shares one page; AgentKit/third parties excluded. Scan changed docs only; +source paths, English prose, diff/localization checks passed, no builds/tests/visual verification. At discovery all +six were pending. Estimates are net production lines including helpers/callers, excluding tests; abandon extractions +that cost more. Operator then authorized R1→R2→R3; R4–R6 were outside that pass, with later R5/R6 updates below. + +### R1. CloudKit record coding — implemented, affected checks passed + +- Evidence: three Cloud coordinators' `makeRecord|envelope|payloadData|systemFields|record|assetURL` methods repeated + record restoration, hashing, inline/assets, secure fields and cache paths; confirm with `rg` in + `Frameworks/TermindInfrastructure/Cloud`. Estimated −70–110, low/medium wire risk. +- `CloudSyncRecordCoding` shares mechanics, raw/quarantine vs validated reads, lowercase SHA-256, lazy assets and + namespaces. Coordinators retain envelope metadata/validation, legacy Guardian canonicalization, errors/public + entry points/zone names/casing/cleanup/independent lifetimes; exact 700,000-byte threshold and frozen models remain. + Actual −40 (1,741→1,701), eight new functions/12 cases covering threshold/storage transitions/hash/corruption/missing + payload/asset errors/secure fields/namespace boundaries. No need for a generic metadata API or R5 due to size alone. +- Pinned macOS 61/61, iOS 13/13, new suites executed with unchanged source snapshots; Vault/Settings/legacy Guardian/ + Metrics/architecture passed. Format/diff passed; Xcode resolved imports SourceKit could not. Evidence under + `/tmp/termind-refactor-three.7kUsqP/r1-focused-*`. Initial full baseline before edits: macOS 1,502/1,505, iOS 502/503; + approval/runtime cancellation, Metrics worker and mobile knowledge expansion failed. No unrelated fixes/package bump. + +### R2. Upload/copy conflicts — implemented with characterized upload hardening + +- Evidence: `remoteConflictPreflight|resolveRemoteDirectories|resolveRemoteConflict|deleteRecursively` in + `SFTPLocalUploadPlanner`, `SFTPRemoteTransferPlanner` and `SFTPRemoteCopyOperation` duplicated subtree/Keep Both/ + delete policy. This is the 09-16 upload follow-up, not unfinished desktop planner adoption. Estimate −100–140; + medium risk because Replace deletes directories. Before sharing, new fake-backend tests reproduced cancelled + directory-only upload creating a directory and Replace accepting an outside child; macOS copy/SFTP 65/65, + mobile upload 5/7 (only new cases failed). +- `SFTPRemoteConflictResolver` uses `RemoteFileSystem`, preserving strict-descendant upload preflight versus + self-or-descendant copy. Upload deliberately gains copy's cancellation/recursive-name/path validation; not + byte-equivalent old behavior. Keep manifests/local scoped traversal/jobs/enqueue/pending UI/interactive deletion/ + directory accounting/errors separate. Copy-source error moved unchanged to its own file. +- Actual −139 (906→767, five files); seven shared functions/17 cases cover both modes/all policies/subtree mapping/ + overwrite/counts/delete order/invalid paths/cancellation. FrameworkTests now links Remote Infrastructure on both + platforms; production graph/pins unchanged. Required mobile coverage retains scoped symlink exclusion and empty + directory uploads. Final focused macOS 81/81, iOS 23/23 include repaired cases/shared suite with unchanged snapshots. + Formatting/plist/localization/diff passed; architecture records resolver/counting rules. Evidence: same root, + `r2-before-*`, `r2-focused-*`; unrelated full failures remain tracked. + +### R3. Pro offer projection — implemented, affected checks passed + +- Evidence: `purchaseButtonTitle|selectedPlanDetail|trialBadge|annualBillingUnit|func formatted` repeated in desktop/ + mobile paywalls. Estimate −20–40, low risk. Value-only `ProOfferPresentation` consumes StoreKit-derived values/ + eligibility/plan, never Infrastructure/controller. Apps retain cards/features/sheets/navigation/purchase/management. +- macOS injects `AppLocalization`, mobile Foundation/Apple bundle lookup; both keep effective locale, Gregorian + full units/one component. Compute once per purchase section; History's abbreviated/two-unit formatter stays separate. + Actual −32 (602→570); nine functions/20 cases cover plan/trial/missing data/period priority/separate locale lookup, + plus six real-catalog English/zh-Hans/zh-Hant AppLanguage cases, with no catalog changes. +- Pinned macOS 41/41, iOS 11/11 with stable sources, including new suites/access/trial/architecture/language tests. + Diff-reviewed native layout/actions; no interactive storefront/screenshots claimed. Evidence: same root `r3-focused-*`. + Repository formatting/localization/diff/plist passed. R1–R3 total −211 production lines, excluding tests/docs and + unrelated `.TermindPresentationQA.xcodeproj` deletion. + +### R1–R3 full verification — gate remained non-green + +Final pinned macOS 1,527/1,530, iOS 528/529, no skips, unchanged action snapshots; all three new shared suites passed +on both. Three failures repeat immutable baseline: +`AgentApprovalQueueTests.cancellingAParallelBatchStillAnswersEveryCall`, +`ServerMetricsLifecycleTests.oneWorkerSurvivesWindowsAndStopsOnPauseSleepAndRemoval`, +`MobileKnowledgeToolRenderingTests.knowledgeRowsStayCompactAtPhoneWidthAndExpandOnDemand`. +Initial `AgentRuntimeTests.stoppingARunCancelsTheToolsItIsWaitingOn` did not recur; no fix claimed. + +Final macOS additionally failed `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable`, +absent in initial baseline despite earlier records. Unchanged HEAD source/test passed both tests in three independent +minimal-package processes; current isolated Xcode macOS failed 1/2, iOS passed 2/2. Clean pinned `8509ddd5` export +with separate DerivedData passed framework-only scheme 2/2 but reproduced 1/2 failure under original TFM scheme. +Thus all four final failures have pre-change evidence; smaller harnesses alone were insufficient. Scheme sensitivity +is established, root cause unrepaired. No unrelated tests/implementation/AgentKit/pins/QA deletion changed for green. +Evidence: `/tmp/termind-refactor-three.7kUsqP/` (`final-*`, `waveform-{baseline,current}-*`, +`waveform-head-tfm.*`, passing `waveform-head-xcode.*`, source manifests). + +### R4. Architecture guards — pending, explicitly structural + +`ArchitectureBoundaryTests` scans AgentKit ownership only in TFM, omits Remote Infrastructure/Presentation/bridge +from SwiftData checks, incompletely forbids Application UI/native I/O and Presentation Infrastructure imports, and +misses annotated/qualified imports. Confirm with `rg 'applicationDoesNotImport|typesOwnedByTheAgentPackage|sharedFrameworksCannot|sharedPresentation|swiftDataModels|swiftFiles|text.split' TFMTests/ArchitectureBoundaryTests.swift`. +Centralize production roots/dependency rules/bounded import recognition; cover all owners, preserving fail-closed +missing/empty roots/native-list allowlist. No production violation/reduction claimed; budget +20–60 test lines. +Use fixtures, never production mutations, to reject annotated/qualified forbidden imports/out-of-owner declarations, +allow valid imports/importer removal. Run boundary/both pinned suites; never restore UI/copy/layout/path/wiring snapshots. + +### R5. CloudKit lifecycle — discovery follow-up, implementing as of 2026-09-19 + +`rg 'operationID|prepareAndSynchronize|synchronizeNow|isReadyToSend|stopEngineForAccountChange|handleZoneDeletion'` +in the three Cloud coordinators confirms duplicated operation/account/engine/fetch/send/pause/reset/error policy. +Existing codecs/journals/merges alone do not characterize async lifecycle. After R1, compose one typed Infrastructure +lifecycle instance per category with value journal projection/closures and injectable account/engine transport. +Keep codecs/merge/quarantine/version acknowledgements/legacy records/Settings secrets in adapters; no universal untyped +framework or model reshaping. Zones/journals/accounts/subscriptions/switches/engines/lifetimes remain independent; +secret uploads require current-engine fetch and revoke on pause/account retirement. Estimate −200–350 additional +production lines including driver/adapters, high risk; package/models unchanged. + +Required deterministic coverage: pause/retry/stale callbacks, account switching/consent, zone/environment resets, +failed fetch/readiness-before-send, secret authorization/revocation; migrate categories individually and retain +tombstone/quarantine/versioned-ack tests. Fresh 09-19 scan reconfirmed evidence; implementation underway, unified +verification follows all requested edits. Architecture ownership is recorded; no completion/result is claimed here. + +### R6. MCP ticket policy — discovery follow-up, implementing as of 2026-09-19 + +`rg 'has.*Activity|count < 64|addingTimeInterval\(600\)|maximumBytes|func (endSession|revokeSession|sweep)|1_800|count - 256'` +in `TFM/Application/MCPServer/MCP{Upload,Download}Coordinator.swift` confirms repeated metadata/activity/expiry/capacity/ +retention; download references `MCPUploadRequest.maximumBytes`. Share only desktop metadata/expiry/retention/limits, reusing +four-slot `MCPTransferBudget`: ten-minute tickets, 64 unfinished/direction, 256 completions/direction/thirty minutes. +Keep separate dictionaries/errors/live states/claims/generations/stream loops/revocation/cleanup/task ordering; +no listener in mobile/frameworks/AgentKit or generic ticket superclass. Upload expiry may spawn cleanup; download +can serve Range after success; `cancelAndWait` captures tasks differently. Estimate originally −30–70, revised on +09-19 to −10–40 including narrow protocol cost; medium/high lifecycle/security risk. + +Retain `MCPUploadTests`, `MCPDownloadTests` and session-teardown tests; add clock-boundary/retention coverage and preserve Range-after-success, +acknowledged offsets, late disconnects, Host/session revocation, slots held through cleanup, protocol strings and +metadata-only audit. Fresh scan reconfirmed evidence; implementing. User requested finishing both remaining refactors +before unified verification, superseding per-edit checks for this pass. + +Scan disposition: R1 first, then R2/R3; R4 before expanding dependency/lifecycle ownership, separate R5/R6 design/ +characterization. Retain frozen schemas, responsibility-named Vault facades, native lists/terminal teardown and local +runtime-version adapters. File size or a small pixel-test duplicate alone never justifies merging native renderers. + +## Window and mobile changes, 2026-09-18 + +### Width-based mobile layouts — implemented, later navigation design superseded below + +`rg 'mobileInterfaceIdiom|MobileInterfaceIdiom' TFI TFITests` found device-based shells/sheets/cards and a duplicate +test root. Removed environment/injection, tested real roots rebuilding native compact/regular shells; estimate −50 +obsolete lines excluding transition behavior/tests. Coverage includes reconstruction/actual selection, Settings/draft +reset, Metrics mapping/Back, Assistant drafts, window independence, deferred saves/errors and live SSH/SFTP. +Phone focused 25 passed; final breadcrumb four Workspace tests passed on phone/tablet. English/zh-Hans/zh-Hant snapshots +and actual rotation reviewed. Device Hub did not perform Stage Manager corner resizing; frame/trait real-root tests +cover width/two-window behavior. Formatting/localization/diff passed, pins unchanged. + +Full iPad: 537 passed/3 failed/0 skipped of 540; macOS: 1,527/1/13 of 1,541, all new tests passed. Clean `c015d5f3` +with original TFI scheme reproduced iPad tab-bar visibility/Host-card height/knowledge expansion; baseline cards pass +on phone. Mac retained unchanged waveform failure. Earlier interrupted cleanup run was superseded by complete iPad. +Evidence: `/tmp/termind-width-{final-ipad,rebuild-macos,iphone,path-final,path-final-ipad,baseline-ipad,baseline-cards}.xcresult`. +Not a green full-suite claim; unrelated assertions/code were not changed. + +### Main-window registration/content style — completed + +`rg` found RootView mounting `MainWindowRegistration` plus `FullHeightWindowContent` solely for two native properties; +independent attachment could size before the full-size coordinate system. Move style/transparency before registration +in the existing macOS bridge, removing representable/NSView (−30/+5). All 47 window/lifecycle/shortcut checks and +formatting/localization/diff passed. Native checks: 904×572 copies exactly, fullscreen Command-N opens Connect, exit/ +close/reopen preserves size; isolated quit/relaunch preserves 1004×652 and last-used 904×572 across differing windows. +System restored one scene; distinct restored frames are test-covered. Same-bundle demo interference prompted clean +relaunch repeats. + +Full pinned immutable `94ad70f1` avoided concurrent Dock-menu test compilation errors: 1,593 passed/5 failed/8 skipped. +Failures: `StoreKitTrialTests.annualProductSuppliesPriceBillingPeriodTrialDurationAndEntitlement` (JPY vs USD), +parallel approval cancellation, Metrics worker lifecycle, +`MetricsJourneyTests.offlineRestoredMetricsReconcileOnMainActor`, +`TerminalCommandDraftControllerTests.readsOneDraftAndRejectsConcurrentRequests`. Only Agent/worker had earlier baseline +evidence; no baseline claim/fix for the other three. Results: +`/tmp/termind-window-sizing-{focused-fixed,snapshot-full}.xcresult`. + +### Metrics clock and short knowledge cards — repaired, full suite still non-green + +Metrics tests advanced the manual clock before GPU work/next sleep registration; pre-fix failed 3/5 and a model probe +needed another 0.05 s. Await both maintenance/sampling sleeps and preserve caller locations in polling; production +Metrics unchanged. Compact SwiftUI details accepted tall proposals; trailing vertical `fixedSize` restores natural +height within scaled cap (short phone fixture 99.33 vs 180 points). Tests classify truncation, checking natural short +height and long expand/layout-retain/collapse, not tool names. + +macOS lifecycle/knowledge 26 functions passed plus 20/20 lifecycle repeats; all three mobile knowledge tests passed +English/zh-Hans/zh-Hant, phone/accessibility; native desktop widths 380/720 retained. Format/localization/diff passed. +Full pinned macOS 1,600 passed/1 failed/8 skipped of 1,609, iOS 584/3/0 of 587; repaired tests passed. Remaining macOS +parallel-approval failure was recorded earlier. Mobile `MobileUploadBatchTests.sftpFolderUploadUsesTheCurrentDirectoryAndOneBatch`, +`MobileEditorToolbarTests.cancelOnlyDismissesUnmodifiedDrafts(isDirty:)`, +`MobileAdaptiveLayoutTests.settingsDepthAndUnsavedFormsResetOnlyAcrossWidthClasses` passed focused reruns, not proof +of baseline/fix. Full mobile bundles finished before optional slow `simctl diagnose` was interrupted; finalized +587-test result is intact. Pins/APIs/persistence unchanged. Evidence: +`/tmp/termind-metrics-knowledge-fix-{macos-focused,repeat20,ios-focused,macos-full,ios-full,ios-unrelated-rerun,ios-upload-rerun}.xcresult`. + +### Preserve mobile navigation during resizing — implemented + +`rg 'layoutTransition|compactMetrics|registrations\(on|scene\.layout' TFI TFITests` found two roots, route resets, +Metrics copies and reconstruction save gates. Replace with one native sidebar-adaptable TabView/five stable stacks; +remove migration/registration/save gating, keep single Metrics shortcut target and save/cancel/error/native terminal +ownership. Estimate −150–200, actual −210 production lines; Metrics/Vault search use `waveform.path.ecg`. + +Focused iPad 21/21; phone 20/21 because a test forced unsupported regular class, changing search placement. Corrected +native-class/compact-width fixture passed three times on each, retaining iPad width transitions. Coverage preserves +pages/Settings/drafts/search/scroll/window independence/save/errors/session instances. English/zh-Hans/zh-Hant normal/ +accessibility screenshots reviewed: five titles fit 320/375-point bottom bars; intermediate iPad top bar scrolls with +all items selectable. Hosting-window/child resize tests do not mistake native title scaling for truncation. Manual +rotation retained Models/icon; Device Hub did not perform corner resizing, covered through frame/trait tests. + +Full before fixture corrections: iPad 583/586, phone 584/586, macOS 1,598 passed/1 failed/8 skipped of 1,607. New label/ +scroll failures were corrected; other failures were reproduced iPad tab visibility/Host-card height and recorded Mac +waveform. No unrelated assertions/production changed; not all-green. Format/localization/diff passed; pins/APIs/stores +unchanged. Evidence: `/tmp/termind-adaptive-tabs-{ipad,iphone,macos}-full.xcresult`, +`/tmp/termind-adaptive-tabs-final-{ipad,iphone}.xcresult`, +`/tmp/termind-adaptive-tabs-scroll-native-{iphone,ipad}.xcresult`. + +### Window/quit confirmation removal — superseded + +`rg 'dismissalConfirmationDialog|Quit with unuploaded changes|sftpEdits.recheck' TFM` found Workspace close and SFTP +quit warnings/five unique strings. Proposed removal preserved native dismissal/pane-first Command-W/ownership/bounded +cleanup, estimated −25–35 production lines/five entries. User instead requested both confirmations retained and only +the edited indicator hidden; both dialogs/all strings restored. + +### Close-button edited indicator — implemented, verification pending + +Restored SwiftUI WindowGroup sets `NSWindow.isDocumentEdited` for Workspace confirmation. Isolated Demo probe cleared +it while retaining Close Window/Cancel. Attached registration now observes/clears decoration only, preserving dismissal +delegate/condition/copy/SFTP quit checks. Visual correction, no code-reduction/API/persistence/package claim. + +### Free Workspace composition — implemented, full verification stopped by user + +`rg 'workspaceComposition|window.proAccess' TFM Frameworks` found 15 gates, window commerce dependency and launch grant. +Remove gates/sticky state, align app/site/StoreKit/docs. Estimate −35–45 production lines plus strings; actual −34/two +entries. All 28 composition cases/seven non-Pro states, six remaining Pro gates and entitlement checks passed, plus +format/localization/site build/diff. English/zh-Hans/zh-Hant desktop/mobile home/FAQ and legacy Chinese redirects reviewed. +User stopped full macOS/iOS tests; before stopping, Mac AgentBackgroundCommand/AgentApprovalQueue/SpeechWaveform failed, +without investigation/rerun. No green full-suite claim; App Store Connect metadata remains a release step. + +## Desktop authentication submission, 2026-09-19 — implemented, full verification pending + +`rg 'continueAuthentication|submittedPassword|let existing = store.identity|passwordUpdate'` in desktop SSH/SFTP models +confirmed duplicated fallback/submission/selected Identity updates/custom reuse/Host association. Share two methods +on existing Application `RemoteSessionHostStore`; sessions retain username validation/passphrase continuation/prompts/ +errors/ephemeral SFTP secrets/transient SSH Hosts/reconnect ordering. Preserve separate Identity-before-Host writes and +secret usage; mobile/Metrics/forwarding keep different save rules. Estimate −40–80, actual −47 production lines; no +new model/base/dependency/schema/secret store or localization change. Architecture updated, boundary rules unchanged; +existing catalog edit stays outside scope. + +Focused 83 functions/105 cases passed: shared policy/desktop entry points, credential usage/preparation/Identity, +Journey/SFTP/window lifetimes/architecture. Initial fixture `Host` ambiguity was fixed with explicit Domain import. +Formatting/localization/diff passed; full pinned macOS/iOS pending. Evidence: +`/tmp/termind-auth-refactor.uUFmoB/focused-macos-2.xcresult` with logs/summary/line counts/dependency/catalog fingerprints. + +## Mobile toolbar policy, 2026-09-19 — implementing + +`rg 'tabBarPlacement|mobileToolbarLayout' TFI` found feature-local Assistant/Metrics fallbacks despite shared stack +environment; production already has no idiom/model layout branches. Remove both reads and consume one shared toolbar +policy, retaining container geometry/active reserved regions/native identity/OS API checks. Estimate −2 production +lines/two duplicate conditions. Native title/menu transitions and fold/session lifetime verification remain pending.