What we are doing: creating the Xcode project and setting exactly the options that the finished app depends on. Two of them (the sandbox and the entitlements) will silently break networking or file access later if they are wrong, so we do them now rather than debugging them later.
What we are doing: everything that touches the network, and the first screen that proves it works. By the end of this part the app lists the models installed on your machine, downloads a new one with a live progress bar and transfer speed, deletes and duplicates models, and shows a proper error banner when Ollama is not running. No chat yet, no database yet — just a real, useful screen.
Why this order: the chat in Part 2 is the same networking code with a different payload. Getting OllamaController, the error vocabulary and the design tokens right first means Part 2 is almost entirely about SwiftUI and SwiftData.
What we are doing: the heart of the app. We define the SwiftData schema (conversations, messages, attachments, folders), own the container explicitly, write the controller that streams a reply token by token, and build the transcript and composer that show it. By the end, you can hold a real conversation, stop it mid-sentence, regenerate it, edit a question and ask again, attach images for a vision model, and quit and relaunch to find everything intact.
What we are doing: turning the placeholder list into the real sidebar. Folders you can create, rename, and drag chats into; a search field that looks inside message bodies and shows you the matching excerpt; per-chat rename, duplicate, delete and Markdown export.
What we are doing: the last two screens. A settings sheet for host, port and timeouts, and a trailing inspector that exposes every sampling parameter per conversation, with a context gauge and the statistics of the last response. We also add the menu commands: ⌘, for settings and ⌘T for a new window tab.
What we are doing: filling in the Ollama SwiftTests target Xcode created in Part 0. There are 105 tests and they run in about a second, because none of them touch the network: the SwiftData ones build an in-memory ModelContainer from ChatStore.schema, and the decoding ones use JSON fixtures copied from a live server.